-This is libusb-1.0.26 from https://libusb.info/.
+This is libusb-1.0.27 from https://libusb.info/.
Since we have such problems with people
getting libusb successfully built - between the Universal Build issues
and the fact that we have to work hard to go find where it's installed
/* Define to 1 to enable message logging. */
#define ENABLE_LOGGING 1
-/* On 10.12 and later, use newly available clock_*() functions */
-#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
-/* Define to 1 if you have the `clock_gettime' function. */
-#define HAVE_CLOCK_GETTIME 1
-#endif
-
/* On 10.6 and later, use newly available pthread_threadid_np() function */
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
/* Define to 1 if you have the 'pthread_threadid_np' function. */
/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
/*
* Core functions for libusb
- * Copyright © 2012-2013 Nathan Hjelm <hjelmn@cs.unm.edu>
+ * Copyright © 2012-2023 Nathan Hjelm <hjelmn@cs.unm.edu>
* Copyright © 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com>
*
static const struct libusb_version libusb_version_internal =
{ LIBUSB_MAJOR, LIBUSB_MINOR, LIBUSB_MICRO, LIBUSB_NANO,
- LIBUSB_RC, "http://libusb.info" };
+ LIBUSB_RC, "https://libusb.info" };
static struct timespec timestamp_origin;
#if defined(ENABLE_LOGGING) && !defined(USE_SYSTEM_LOGGING_FACILITY)
static libusb_log_cb log_handler;
struct libusb_context *usbi_default_context;
struct libusb_context *usbi_fallback_context;
static int default_context_refcnt;
+#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
+static usbi_atomic_t default_debug_level = -1;
+#endif
static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER;
static struct usbi_option default_context_options[LIBUSB_OPTION_MAX];
*
* libusb is an open source library that allows you to communicate with USB
* devices from user space. For more info, see the
- * <a href="http://libusb.info">libusb homepage</a>.
+ * <a href="https://libusb.info">libusb homepage</a>.
*
* This documentation is aimed at application developers wishing to
* communicate with USB peripherals from their own software. After reviewing
* this documentation, feedback and questions can be sent to the
- * <a href="http://mailing-list.libusb.info">libusb-devel mailing list</a>.
+ * <a href="https://mailing-list.libusb.info">libusb-devel mailing list</a>.
*
* This documentation assumes knowledge of how to operate USB devices from
* a software standpoint (descriptors, configurations, interfaces, endpoints,
* libusb uses stderr for all logging. By default, logging is set to NONE,
* which means that no output will be produced. However, unless the library
* has been compiled with logging disabled, then any application calls to
- * libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level), or the setting of the
- * environmental variable LIBUSB_DEBUG outside of the application, can result
- * in logging being produced. Your application should therefore not close
- * stderr, but instead direct it to the null device if its output is
- * undesirable.
- *
- * The libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) function can be
- * used to enable logging of certain messages. Under standard configuration,
- * libusb doesn't really log much so you are advised to use this function
- * to enable all error/warning/ informational messages. It will help debug
- * problems with your software.
+ * libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level),
+ * libusb_init_context, or the setting of the environmental variable
+ * LIBUSB_DEBUG outside of the application, can result in logging being
+ * produced. Your application should therefore not close stderr, but instead
+ * direct it to the null device if its output is undesirable.
+ *
+ * The libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) or
+ * libusb_init_context functions can be used to enable logging of certain
+ * messages. With the default configuration, libusb will not log much so if
+ * you are advised to use one of these functions to enable all
+ * error/warning/informational messages. It will help debug problems with your
+ * software.
*
* The logged messages are unstructured. There is no one-to-one correspondence
* between messages being logged and success or failure return codes from
* The LIBUSB_DEBUG environment variable can be used to enable message logging
* at run-time. This environment variable should be set to a log level number,
* which is interpreted the same as the
- * libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) parameter. When this
- * environment variable is set, the message logging verbosity level is fixed
- * and libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) effectively does
- * nothing.
+ * libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level), or
+ * libusb_init_context(&ctx, &(struct libusb_init_option){.option = LIBUSB_OPTION_LOG_LEVEL, .value = {.ival = level}}, 0).
+ * When the environment variable is set, the message logging verbosity level is
+ * fixed and setting the LIBUSB_OPTION_LOG_LEVEL option has no effect.
*
* libusb can be compiled without any logging functions, useful for embedded
- * systems. In this case, libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level)
- * and the LIBUSB_DEBUG environment variable have no effects.
+ * systems. In this case, neither the LIBUSB_OPTION_LOG_LEVEL option, nor the
+ * LIBUSB_DEBUG environment variable will have any effect.
*
* libusb can also be compiled with verbose debugging messages always. When
* the library is compiled in this way, all messages of all verbosities are
- * always logged. libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) and
- * the LIBUSB_DEBUG environment variable have no effects.
+ * always logged. Again, in this case, neither the LIBUSB_OPTION_LOG_LEVEL
+ * option, nor the LIBUSB_DEBUG environment variable will have any effect.
*
* \section remarks Other remarks
*
* developed modules may both use libusb.
*
* libusb is written to allow for these multiple user scenarios. The two
- * "instances" of libusb will not interfere: libusb_set_option() calls
- * from one user will not affect the same settings for other users, other
- * users can continue using libusb after one of them calls libusb_exit(), etc.
+ * "instances" of libusb will not interfere: an option set by one user will have
+ * no effect the same option for other users, other users can continue using
+ * libusb after one of them calls libusb_exit(), etc.
*
* This is made possible through libusb's <em>context</em> concept. When you
- * call libusb_init(), you are (optionally) given a context. You can then pass
+ * call libusb_init_context(), you are (optionally) given a context. You can then pass
* this context pointer back into future libusb functions.
*
* In order to keep things simple for more simplistic applications, it is
* legal to pass NULL to all functions requiring a context pointer (as long as
* you're sure no other code will attempt to use libusb from the same process).
* When you pass NULL, the default context will be used. The default context
- * is created the first time a process calls libusb_init() when no other
+ * is created the first time a process calls libusb_init_context() when no other
* context is alive. Contexts are destroyed during libusb_exit().
*
* The default context is reference-counted and can be shared. That means that
- * if libusb_init(NULL) is called twice within the same process, the two
+ * if libusb_init_context(NULL, x, y) is called twice within the same process, the two
* users end up sharing the same context. The deinitialization and freeing of
* the default context will only happen when the last user calls libusb_exit().
* In other words, the default context is created and initialized when its
* - libusb_get_device_speed()
* - libusb_get_iso_packet_buffer()
* - libusb_get_iso_packet_buffer_simple()
+ * - libusb_get_max_alt_packet_size()
* - libusb_get_max_iso_packet_size()
* - libusb_get_max_packet_size()
* - libusb_get_next_timeout()
* - libusb_hotplug_deregister_callback()
* - libusb_hotplug_register_callback()
* - libusb_init()
+ * - libusb_init_context()
* - libusb_interrupt_event_handler()
* - libusb_interrupt_transfer()
* - libusb_kernel_driver_active()
/** \ingroup libusb_dev
* Get the list of all port numbers from root for the specified device
*
- * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102
+ * Since version 1.0.16, \ref LIBUSBX_API_VERSION >= 0x01000102
* \param dev a device
* \param port_numbers the array that should contain the port numbers
* \param port_numbers_len the maximum length of the array. As per the USB 3.0
* specs, the current maximum limit for the depth is 7.
* \returns the number of elements filled
- * \returns LIBUSB_ERROR_OVERFLOW if the array is too small
+ * \returns \ref LIBUSB_ERROR_OVERFLOW if the array is too small
*/
int API_EXPORTED libusb_get_port_numbers(libusb_device *dev,
uint8_t *port_numbers, int port_numbers_len)
* \param dev a device
* \param endpoint address of the endpoint in question
* \returns the wMaxPacketSize value
- * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist
- * \returns LIBUSB_ERROR_OTHER on other failure
+ * \returns \ref LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist
+ * \returns \ref LIBUSB_ERROR_OTHER on other failure
*/
int API_EXPORTED libusb_get_max_packet_size(libusb_device *dev,
unsigned char endpoint)
return r;
}
+static const struct libusb_endpoint_descriptor *find_alt_endpoint(
+ struct libusb_config_descriptor *config,
+ int iface_idx, int altsetting_idx, unsigned char endpoint)
+{
+ if (iface_idx >= config->bNumInterfaces) {
+ return NULL;
+ }
+
+ const struct libusb_interface *iface = &config->interface[iface_idx];
+
+ if (altsetting_idx >= iface->num_altsetting) {
+ return NULL;
+ }
+
+ const struct libusb_interface_descriptor *altsetting
+ = &iface->altsetting[altsetting_idx];
+ int ep_idx;
+
+ for (ep_idx = 0; ep_idx < altsetting->bNumEndpoints; ep_idx++) {
+ const struct libusb_endpoint_descriptor *ep =
+ &altsetting->endpoint[ep_idx];
+ if (ep->bEndpointAddress == endpoint)
+ return ep;
+ }
+ return NULL;
+}
+
+static int get_endpoint_max_packet_size(libusb_device *dev,
+ const struct libusb_endpoint_descriptor *ep)
+{
+ struct libusb_ss_endpoint_companion_descriptor *ss_ep_cmp;
+ enum libusb_endpoint_transfer_type ep_type;
+ uint16_t val;
+ int r = 0;
+ int speed;
+
+ speed = libusb_get_device_speed(dev);
+ if (speed >= LIBUSB_SPEED_SUPER) {
+ r = libusb_get_ss_endpoint_companion_descriptor(dev->ctx, ep, &ss_ep_cmp);
+ if (r == LIBUSB_SUCCESS) {
+ r = ss_ep_cmp->wBytesPerInterval;
+ libusb_free_ss_endpoint_companion_descriptor(ss_ep_cmp);
+ }
+ }
+
+ /* If the device isn't a SuperSpeed device or retrieving the SS endpoint didn't worked. */
+ if (speed < LIBUSB_SPEED_SUPER || r < 0) {
+ val = ep->wMaxPacketSize;
+ ep_type = (enum libusb_endpoint_transfer_type) (ep->bmAttributes & 0x3);
+
+ r = val & 0x07ff;
+ if (ep_type == LIBUSB_ENDPOINT_TRANSFER_TYPE_ISOCHRONOUS
+ || ep_type == LIBUSB_ENDPOINT_TRANSFER_TYPE_INTERRUPT)
+ r *= (1 + ((val >> 11) & 3));
+ }
+
+ return r;
+}
+
/** \ingroup libusb_dev
* Calculate the maximum packet size which a specific endpoint is capable is
* sending or receiving in the duration of 1 microframe
* libusb_set_iso_packet_lengths() in order to set the length field of every
* isochronous packet in a transfer.
*
+ * This function only considers the first alternate setting of the interface.
+ * If the endpoint has different maximum packet sizes for different alternate
+ * settings, you probably want libusb_get_max_alt_packet_size() instead.
+ *
* Since v1.0.3.
*
* \param dev a device
* \param endpoint address of the endpoint in question
* \returns the maximum packet size which can be sent/received on this endpoint
- * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist
- * \returns LIBUSB_ERROR_OTHER on other failure
+ * \returns \ref LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist
+ * \returns \ref LIBUSB_ERROR_OTHER on other failure
+ * \see libusb_get_max_alt_packet_size
*/
int API_EXPORTED libusb_get_max_iso_packet_size(libusb_device *dev,
unsigned char endpoint)
{
struct libusb_config_descriptor *config;
const struct libusb_endpoint_descriptor *ep;
- struct libusb_ss_endpoint_companion_descriptor *ss_ep_cmp;
- enum libusb_endpoint_transfer_type ep_type;
- uint16_t val;
int r;
- int speed;
r = libusb_get_active_config_descriptor(dev, &config);
if (r < 0) {
goto out;
}
- speed = libusb_get_device_speed(dev);
- if (speed >= LIBUSB_SPEED_SUPER) {
- r = libusb_get_ss_endpoint_companion_descriptor(dev->ctx, ep, &ss_ep_cmp);
- if (r == LIBUSB_SUCCESS) {
- r = ss_ep_cmp->wBytesPerInterval;
- libusb_free_ss_endpoint_companion_descriptor(ss_ep_cmp);
- }
- }
+ r = get_endpoint_max_packet_size(dev, ep);
- /* If the device isn't a SuperSpeed device or retrieving the SS endpoint didn't worked. */
- if (speed < LIBUSB_SPEED_SUPER || r < 0) {
- val = ep->wMaxPacketSize;
- ep_type = (enum libusb_endpoint_transfer_type) (ep->bmAttributes & 0x3);
+out:
+ libusb_free_config_descriptor(config);
+ return r;
+}
- r = val & 0x07ff;
- if (ep_type == LIBUSB_ENDPOINT_TRANSFER_TYPE_ISOCHRONOUS
- || ep_type == LIBUSB_ENDPOINT_TRANSFER_TYPE_INTERRUPT)
- r *= (1 + ((val >> 11) & 3));
+/** \ingroup libusb_dev
+ * Calculate the maximum packet size which a specific endpoint is capable of
+ * sending or receiving in the duration of 1 microframe
+ *
+ * Only the active configuration is examined. The calculation is based on the
+ * wMaxPacketSize field in the endpoint descriptor as described in section
+ * 9.6.6 in the USB 2.0 specifications.
+ *
+ * If acting on an isochronous or interrupt endpoint, this function will
+ * multiply the value found in bits 0:10 by the number of transactions per
+ * microframe (determined by bits 11:12). Otherwise, this function just
+ * returns the numeric value found in bits 0:10. For USB 3.0 device, it
+ * will attempts to retrieve the Endpoint Companion Descriptor to return
+ * wBytesPerInterval.
+ *
+ * This function is useful for setting up isochronous transfers, for example
+ * you might pass the return value from this function to
+ * libusb_set_iso_packet_lengths() in order to set the length field of every
+ * isochronous packet in a transfer.
+ *
+ * Since version 1.0.27, \ref LIBUSB_API_VERSION >= 0x0100010A
+ *
+ * \param dev a device
+ * \param interface_number the <tt>bInterfaceNumber</tt> of the interface
+ * the endpoint belongs to
+ * \param alternate_setting the <tt>bAlternateSetting</tt> of the interface
+ * \param endpoint address of the endpoint in question
+ * \returns the maximum packet size which can be sent/received on this endpoint
+ * \returns \ref LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist
+ * \returns \ref LIBUSB_ERROR_OTHER on other failure
+ * \see libusb_get_max_iso_packet_size
+ */
+int API_EXPORTED libusb_get_max_alt_packet_size(libusb_device *dev,
+ int interface_number, int alternate_setting, unsigned char endpoint)
+{
+ struct libusb_config_descriptor *config;
+ const struct libusb_endpoint_descriptor *ep;
+ int r;
+
+ r = libusb_get_active_config_descriptor(dev, &config);
+ if (r < 0) {
+ usbi_err(DEVICE_CTX(dev),
+ "could not retrieve active config descriptor");
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ ep = find_alt_endpoint(config, interface_number,
+ alternate_setting, endpoint);
+ if (!ep) {
+ r = LIBUSB_ERROR_NOT_FOUND;
+ goto out;
}
+ r = get_endpoint_max_packet_size(dev, ep);
+
out:
libusb_free_config_descriptor(config);
return r;
* handle for the underlying device. The handle allows you to use libusb to
* perform I/O on the device in question.
*
- * Call libusb_set_option(NULL, LIBUSB_OPTION_NO_DEVICE_DISCOVERY) before
- * libusb_init() if you want to skip enumeration of USB devices. In particular,
- * this might be needed on Android if you don't have authority to access USB
- * devices in general.
+ * Call libusb_init_context with the LIBUSB_OPTION_NO_DEVICE_DISCOVERY
+ * option if you want to skip enumeration of USB devices. In particular, this
+ * might be needed on Android if you don't have authority to access USB
+ * devices in general. Setting this option with libusb_set_option is deprecated.
*
* On Linux, the system device handle must be a valid file descriptor opened
* on the device node.
* \param dev_handle output location for the returned device handle pointer. Only
* populated when the return code is 0.
* \returns 0 on success
- * \returns LIBUSB_ERROR_NO_MEM on memory allocation failure
- * \returns LIBUSB_ERROR_ACCESS if the user has insufficient permissions
- * \returns LIBUSB_ERROR_NOT_SUPPORTED if the operation is not supported on this
+ * \returns \ref LIBUSB_ERROR_NO_MEM on memory allocation failure
+ * \returns \ref LIBUSB_ERROR_ACCESS if the user has insufficient permissions
+ * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED if the operation is not supported on this
* platform
* \returns another LIBUSB_ERROR code on other failure
*/
* \param dev_handle output location for the returned device handle pointer. Only
* populated when the return code is 0.
* \returns 0 on success
- * \returns LIBUSB_ERROR_NO_MEM on memory allocation failure
- * \returns LIBUSB_ERROR_ACCESS if the user has insufficient permissions
- * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns \ref LIBUSB_ERROR_NO_MEM on memory allocation failure
+ * \returns \ref LIBUSB_ERROR_ACCESS if the user has insufficient permissions
+ * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
* \returns another LIBUSB_ERROR code on other failure
*/
int API_EXPORTED libusb_open(libusb_device *dev,
for_each_transfer_safe(ctx, itransfer, tmp) {
struct libusb_transfer *transfer =
USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ uint32_t state_flags;
if (transfer->dev_handle != dev_handle)
continue;
usbi_mutex_lock(&itransfer->lock);
- if (!(itransfer->state_flags & USBI_TRANSFER_DEVICE_DISAPPEARED)) {
+ state_flags = itransfer->state_flags;
+ usbi_mutex_unlock(&itransfer->lock);
+ if (!(state_flags & USBI_TRANSFER_DEVICE_DISAPPEARED)) {
usbi_err(ctx, "Device handle closed while transfer was still being processed, but the device is still connected as far as we know");
- if (itransfer->state_flags & USBI_TRANSFER_CANCELLING)
+ if (state_flags & USBI_TRANSFER_CANCELLING)
usbi_warn(ctx, "A cancellation for an in-flight transfer hasn't completed but closing the device handle");
else
usbi_err(ctx, "A cancellation hasn't even been scheduled on the transfer for which the device is closing");
}
- usbi_mutex_unlock(&itransfer->lock);
/* remove from the list of in-flight transfers and make sure
* we don't accidentally use the device handle in the future
* the device handle is invalid
*/
usbi_dbg(ctx, "Removed transfer %p from the in-flight list because device handle %p closed",
- transfer, dev_handle);
+ (void *) transfer, (void *) dev_handle);
}
usbi_mutex_unlock(&ctx->flying_transfers_lock);
* \param config output location for the bConfigurationValue of the active
* configuration (only valid for return code 0)
* \returns 0 on success
- * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
* \returns another LIBUSB_ERROR code on other failure
*/
int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle,
* endpoint halts cleared, toggles reset).
*
* Not all backends support setting the configuration from user space, which
- * will be indicated by the return code LIBUSB_ERROR_NOT_SUPPORTED. As this
+ * will be indicated by the return code \ref LIBUSB_ERROR_NOT_SUPPORTED. As this
* suggests that the platform is handling the device configuration itself,
* this error should generally be safe to ignore.
*
* wish to activate, or -1 if you wish to put the device in an unconfigured
* state
* \returns 0 on success
- * \returns LIBUSB_ERROR_NOT_FOUND if the requested configuration does not exist
- * \returns LIBUSB_ERROR_BUSY if interfaces are currently claimed
- * \returns LIBUSB_ERROR_NOT_SUPPORTED if setting or changing the configuration
+ * \returns \ref LIBUSB_ERROR_NOT_FOUND if the requested configuration does not exist
+ * \returns \ref LIBUSB_ERROR_BUSY if interfaces are currently claimed
+ * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED if setting or changing the configuration
* is not supported by the backend
- * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
* \returns another LIBUSB_ERROR code on other failure
* \see libusb_set_auto_detach_kernel_driver()
*/
* \param interface_number the <tt>bInterfaceNumber</tt> of the interface you
* wish to claim
* \returns 0 on success
- * \returns LIBUSB_ERROR_NOT_FOUND if the requested interface does not exist
- * \returns LIBUSB_ERROR_BUSY if another program or driver has claimed the
+ * \returns \ref LIBUSB_ERROR_NOT_FOUND if the requested interface does not exist
+ * \returns \ref LIBUSB_ERROR_BUSY if another program or driver has claimed the
* interface
- * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
* \returns a LIBUSB_ERROR code on other failure
* \see libusb_set_auto_detach_kernel_driver()
*/
* \param interface_number the <tt>bInterfaceNumber</tt> of the
* previously-claimed interface
* \returns 0 on success
- * \returns LIBUSB_ERROR_NOT_FOUND if the interface was not claimed
- * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns \ref LIBUSB_ERROR_NOT_FOUND if the interface was not claimed
+ * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
* \returns another LIBUSB_ERROR code on other failure
* \see libusb_set_auto_detach_kernel_driver()
*/
* \param alternate_setting the <tt>bAlternateSetting</tt> of the alternate
* setting to activate
* \returns 0 on success
- * \returns LIBUSB_ERROR_NOT_FOUND if the interface was not claimed, or the
+ * \returns \ref LIBUSB_ERROR_NOT_FOUND if the interface was not claimed, or the
* requested alternate setting does not exist
- * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
* \returns another LIBUSB_ERROR code on other failure
*/
int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_handle,
return LIBUSB_ERROR_INVALID_PARAM;
if (!usbi_atomic_load(&dev_handle->dev->attached)) {
- usbi_mutex_unlock(&dev_handle->lock);
return LIBUSB_ERROR_NO_DEVICE;
}
* \param dev_handle a device handle
* \param endpoint the endpoint to clear halt status
* \returns 0 on success
- * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist
- * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns \ref LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist
+ * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
* \returns another LIBUSB_ERROR code on other failure
*/
int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle,
* If the reset fails, the descriptors change, or the previous state cannot be
* restored, the device will appear to be disconnected and reconnected. This
* means that the device handle is no longer valid (you should close it) and
- * rediscover the device. A return code of LIBUSB_ERROR_NOT_FOUND indicates
+ * rediscover the device. A return code of \ref LIBUSB_ERROR_NOT_FOUND indicates
* when this is the case.
*
* This is a blocking function which usually incurs a noticeable delay.
*
* \param dev_handle a handle of the device to reset
* \returns 0 on success
- * \returns LIBUSB_ERROR_NOT_FOUND if re-enumeration is required, or if the
+ * \returns \ref LIBUSB_ERROR_NOT_FOUND if re-enumeration is required, or if the
* device has been disconnected
* \returns another LIBUSB_ERROR code on other failure
*/
* \param dev_handle a device handle
* \param endpoints array of endpoints to free streams on
* \param num_endpoints length of the endpoints array
- * \returns LIBUSB_SUCCESS, or a LIBUSB_ERROR code on failure
+ * \returns \ref LIBUSB_SUCCESS, or a LIBUSB_ERROR code on failure
*/
int API_EXPORTED libusb_free_streams(libusb_device_handle *dev_handle,
unsigned char *endpoints, int num_endpoints)
* \param dev_handle a device handle
* \param buffer pointer to the previously allocated memory
* \param length size of previously allocated memory
- * \returns LIBUSB_SUCCESS, or a LIBUSB_ERROR code on failure
+ * \returns \ref LIBUSB_SUCCESS, or a LIBUSB_ERROR code on failure
*/
int API_EXPORTED libusb_dev_mem_free(libusb_device_handle *dev_handle,
unsigned char *buffer, size_t length)
* \param interface_number the interface to check
* \returns 0 if no kernel driver is active
* \returns 1 if a kernel driver is active
- * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
- * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality
+ * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality
* is not available
* \returns another LIBUSB_ERROR code on other failure
* \see libusb_detach_kernel_driver()
*
* Note that libusb itself also talks to the device through a special kernel
* driver, if this driver is already attached to the device, this call will
- * not detach it and return LIBUSB_ERROR_NOT_FOUND.
+ * not detach it and return \ref LIBUSB_ERROR_NOT_FOUND.
*
* \param dev_handle a device handle
* \param interface_number the interface to detach the driver from
* \returns 0 on success
- * \returns LIBUSB_ERROR_NOT_FOUND if no kernel driver was active
- * \returns LIBUSB_ERROR_INVALID_PARAM if the interface does not exist
- * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
- * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality
+ * \returns \ref LIBUSB_ERROR_NOT_FOUND if no kernel driver was active
+ * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the interface does not exist
+ * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality
* is not available
* \returns another LIBUSB_ERROR code on other failure
* \see libusb_kernel_driver_active()
* \param dev_handle a device handle
* \param interface_number the interface to attach the driver from
* \returns 0 on success
- * \returns LIBUSB_ERROR_NOT_FOUND if no kernel driver was active
- * \returns LIBUSB_ERROR_INVALID_PARAM if the interface does not exist
- * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
- * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality
+ * \returns \ref LIBUSB_ERROR_NOT_FOUND if no kernel driver was active
+ * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the interface does not exist
+ * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality
* is not available
- * \returns LIBUSB_ERROR_BUSY if the driver cannot be attached because the
+ * \returns \ref LIBUSB_ERROR_BUSY if the driver cannot be attached because the
* interface is claimed by a program or driver
* \returns another LIBUSB_ERROR code on other failure
* \see libusb_kernel_driver_active()
* handles by default.
*
* On platforms which do not have LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER
- * this function will return LIBUSB_ERROR_NOT_SUPPORTED, and libusb will
+ * this function will return \ref LIBUSB_ERROR_NOT_SUPPORTED, and libusb will
* continue as if this function was never called.
*
* \param dev_handle a device handle
* \param enable whether to enable or disable auto kernel driver detachment
*
- * \returns LIBUSB_SUCCESS on success
- * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality
+ * \returns \ref LIBUSB_SUCCESS on success
+ * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality
* is not available
* \see libusb_claim_interface()
* \see libusb_release_interface()
}
/** \ingroup libusb_lib
- * \deprecated Use libusb_set_option() instead using the
- * \ref LIBUSB_OPTION_LOG_LEVEL option.
+ * Deprecated. Use libusb_set_option() or libusb_init_context() instead,
+ * with the \ref LIBUSB_OPTION_LOG_LEVEL option.
*/
void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level)
{
-#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
- ctx = usbi_get_context(ctx);
- if (!ctx->debug_fixed) {
- level = CLAMP(level, LIBUSB_LOG_LEVEL_NONE, LIBUSB_LOG_LEVEL_DEBUG);
- ctx->debug = (enum libusb_log_level)level;
+ libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level);
+}
+
+static void libusb_set_log_cb_internal(libusb_context *ctx, libusb_log_cb cb,
+ int mode)
+{
+#if defined(ENABLE_LOGGING) && (!defined(ENABLE_DEBUG_LOGGING) || !defined(USE_SYSTEM_LOGGING_FACILITY))
+#if !defined(USE_SYSTEM_LOGGING_FACILITY)
+ if (mode & LIBUSB_LOG_CB_GLOBAL)
+ log_handler = cb;
+#endif
+#if !defined(ENABLE_DEBUG_LOGGING)
+ if (mode & LIBUSB_LOG_CB_CONTEXT) {
+ ctx = usbi_get_context(ctx);
+ ctx->log_handler = cb;
}
#else
UNUSED(ctx);
- UNUSED(level);
+#endif
+#else
+ UNUSED(ctx);
+ UNUSED(cb);
+ UNUSED(mode);
#endif
}
void API_EXPORTED libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb,
int mode)
{
-#if defined(ENABLE_LOGGING) && (!defined(ENABLE_DEBUG_LOGGING) || !defined(USE_SYSTEM_LOGGING_FACILITY))
-#if !defined(USE_SYSTEM_LOGGING_FACILITY)
- if (mode & LIBUSB_LOG_CB_GLOBAL)
- log_handler = cb;
-#endif
-#if !defined(ENABLE_DEBUG_LOGGING)
- if (mode & LIBUSB_LOG_CB_CONTEXT) {
- ctx = usbi_get_context(ctx);
- ctx->log_handler = cb;
- }
-#else
- UNUSED(ctx);
-#endif
-#else
- UNUSED(ctx);
- UNUSED(cb);
- UNUSED(mode);
-#endif
+ libusb_set_log_cb_internal(ctx, cb, mode);
}
/** \ingroup libusb_lib
* \param option which option to set
* \param ... any required arguments for the specified option
*
- * \returns LIBUSB_SUCCESS on success
- * \returns LIBUSB_ERROR_INVALID_PARAM if the option or arguments are invalid
- * \returns LIBUSB_ERROR_NOT_SUPPORTED if the option is valid but not supported
+ * \returns \ref LIBUSB_SUCCESS on success
+ * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the option or arguments are invalid
+ * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED if the option is valid but not supported
* on this platform
- * \returns LIBUSB_ERROR_NOT_FOUND if LIBUSB_OPTION_USE_USBDK is valid on this platform but UsbDk is not available
+ * \returns \ref LIBUSB_ERROR_NOT_FOUND if LIBUSB_OPTION_USE_USBDK is valid on this platform but UsbDk is not available
*/
-int API_EXPORTED libusb_set_option(libusb_context *ctx,
+int API_EXPORTEDV libusb_set_option(libusb_context *ctx,
enum libusb_option option, ...)
{
int arg = 0, r = LIBUSB_SUCCESS;
+ libusb_log_cb log_cb = NULL;
va_list ap;
+#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
+ int is_default_context = (NULL == ctx);
+#endif
va_start(ap, option);
+
if (LIBUSB_OPTION_LOG_LEVEL == option) {
arg = va_arg(ap, int);
if (arg < LIBUSB_LOG_LEVEL_NONE || arg > LIBUSB_LOG_LEVEL_DEBUG) {
r = LIBUSB_ERROR_INVALID_PARAM;
}
}
- va_end(ap);
-
- if (LIBUSB_SUCCESS != r) {
- return r;
+ if (LIBUSB_OPTION_LOG_CB == option) {
+ log_cb = (libusb_log_cb) va_arg(ap, libusb_log_cb);
}
- if (option >= LIBUSB_OPTION_MAX) {
- return LIBUSB_ERROR_INVALID_PARAM;
- }
+ do {
+ if (LIBUSB_SUCCESS != r) {
+ break;
+ }
- if (NULL == ctx) {
- usbi_mutex_static_lock(&default_context_lock);
- default_context_options[option].is_set = 1;
- if (LIBUSB_OPTION_LOG_LEVEL == option) {
- default_context_options[option].arg.ival = arg;
+ if (option >= LIBUSB_OPTION_MAX) {
+ r = LIBUSB_ERROR_INVALID_PARAM;
+ break;
}
- usbi_mutex_static_unlock(&default_context_lock);
- }
- ctx = usbi_get_context(ctx);
- if (NULL == ctx) {
- return LIBUSB_SUCCESS;
- }
+ if (NULL == ctx) {
+ usbi_mutex_static_lock(&default_context_lock);
+ default_context_options[option].is_set = 1;
+ if (LIBUSB_OPTION_LOG_LEVEL == option) {
+ default_context_options[option].arg.ival = arg;
+ } else if (LIBUSB_OPTION_LOG_CB == option) {
+ default_context_options[option].arg.log_cbval = log_cb;
+ libusb_set_log_cb_internal(NULL, log_cb, LIBUSB_LOG_CB_GLOBAL);
+ }
+ usbi_mutex_static_unlock(&default_context_lock);
+ }
- switch (option) {
- case LIBUSB_OPTION_LOG_LEVEL:
+ ctx = usbi_get_context(ctx);
+ if (NULL == ctx)
+ break;
+
+ switch (option) {
+ case LIBUSB_OPTION_LOG_LEVEL:
#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
- if (!ctx->debug_fixed)
- ctx->debug = (enum libusb_log_level)arg;
+ if (!ctx->debug_fixed) {
+ ctx->debug = (enum libusb_log_level)arg;
+ if (is_default_context)
+ usbi_atomic_store(&default_debug_level, CLAMP(arg, LIBUSB_LOG_LEVEL_NONE, LIBUSB_LOG_LEVEL_DEBUG));
+ }
#endif
- break;
+ break;
- /* Handle all backend-specific options here */
- case LIBUSB_OPTION_USE_USBDK:
- case LIBUSB_OPTION_NO_DEVICE_DISCOVERY:
- if (usbi_backend.set_option)
- return usbi_backend.set_option(ctx, option, ap);
+ /* Handle all backend-specific options here */
+ case LIBUSB_OPTION_USE_USBDK:
+ case LIBUSB_OPTION_NO_DEVICE_DISCOVERY:
+ if (usbi_backend.set_option) {
+ r = usbi_backend.set_option(ctx, option, ap);
+ break;
+ }
- return LIBUSB_ERROR_NOT_SUPPORTED;
- break;
+ r = LIBUSB_ERROR_NOT_SUPPORTED;
+ break;
- case LIBUSB_OPTION_MAX:
- default:
- return LIBUSB_ERROR_INVALID_PARAM;
- }
+ case LIBUSB_OPTION_LOG_CB:
+ libusb_set_log_cb_internal(ctx, log_cb, LIBUSB_LOG_CB_CONTEXT);
+ break;
- return LIBUSB_SUCCESS;;
+ case LIBUSB_OPTION_MAX: /* unreachable */
+ default:
+ r = LIBUSB_ERROR_INVALID_PARAM;
+ }
+ } while (0);
+
+ va_end(ap);
+
+ return r;
}
#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
}
#endif
+/** \ingroup libusb_lib
+ * Deprecated initialization function. Equivalent to calling libusb_init_context with no options.
+ *
+ * \see libusb_init_context
+ */
+int API_EXPORTED libusb_init(libusb_context **ctx)
+{
+ return libusb_init_context(ctx, NULL, 0);
+}
+
/** \ingroup libusb_lib
* Initialize libusb. This function must be called before calling any other
* libusb function.
*
* If you do not provide an output location for a context pointer, a default
* context will be created. If there was already a default context, it will
- * be reused (and nothing will be initialized/reinitialized).
+ * be reused (and nothing will be initialized/reinitialized and options will
+ * be ignored). If num_options is 0 then options is ignored and may be NULL.
+ *
+ * Since version 1.0.27, \ref LIBUSB_API_VERSION >= 0x0100010A
*
* \param ctx Optional output location for context pointer.
* Only valid on return code 0.
+ * \param options Optional array of options to set on the new context.
+ * \param num_options Number of elements in the options array.
* \returns 0 on success, or a LIBUSB_ERROR code on failure
* \see libusb_contexts
*/
-int API_EXPORTED libusb_init(libusb_context **ctx)
+int API_EXPORTED libusb_init_context(libusb_context **ctx, const struct libusb_init_option options[], int num_options)
{
size_t priv_size = usbi_backend.context_priv_size;
struct libusb_context *_ctx;
}
/* check for first init */
+ usbi_mutex_static_lock(&active_contexts_lock);
if (!active_contexts_list.next) {
list_init(&active_contexts_list);
usbi_get_monotonic_time(×tamp_origin);
}
+ usbi_mutex_static_unlock(&active_contexts_lock);
_ctx = calloc(1, PTR_ALIGN(sizeof(*_ctx)) + priv_size);
if (!_ctx) {
}
#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
- if (NULL == ctx && default_context_options[LIBUSB_OPTION_LOG_LEVEL].is_set) {
- _ctx->debug = default_context_options[LIBUSB_OPTION_LOG_LEVEL].arg.ival;
- } else {
+ _ctx->debug = LIBUSB_LOG_LEVEL_NONE;
+ if (getenv("LIBUSB_DEBUG")) {
_ctx->debug = get_env_debug_level();
- }
- if (_ctx->debug != LIBUSB_LOG_LEVEL_NONE)
_ctx->debug_fixed = 1;
+ } else if (default_context_options[LIBUSB_OPTION_LOG_LEVEL].is_set) {
+ _ctx->debug = default_context_options[LIBUSB_OPTION_LOG_LEVEL].arg.ival;
+ }
#endif
usbi_mutex_init(&_ctx->usb_devs_lock);
if (LIBUSB_OPTION_LOG_LEVEL == option || !default_context_options[option].is_set) {
continue;
}
- r = libusb_set_option(_ctx, option);
+ if (LIBUSB_OPTION_LOG_CB != option) {
+ r = libusb_set_option(_ctx, option);
+ } else {
+ r = libusb_set_option(_ctx, option, default_context_options[option].arg.log_cbval);
+ }
+ if (LIBUSB_SUCCESS != r)
+ goto err_free_ctx;
+ }
+
+ /* apply any options provided by the user */
+ for (int i = 0 ; i < num_options ; ++i) {
+ switch(options[i].option) {
+ case LIBUSB_OPTION_LOG_CB:
+ r = libusb_set_option(_ctx, options[i].option, options[i].value.log_cbval);
+ break;
+
+ case LIBUSB_OPTION_LOG_LEVEL:
+ case LIBUSB_OPTION_USE_USBDK:
+ case LIBUSB_OPTION_NO_DEVICE_DISCOVERY:
+ case LIBUSB_OPTION_MAX:
+ default:
+ r = libusb_set_option(_ctx, options[i].option, options[i].value.ival);
+ }
if (LIBUSB_SUCCESS != r)
goto err_free_ctx;
}
if (!ctx) {
usbi_default_context = _ctx;
default_context_refcnt = 1;
+#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
+ usbi_atomic_store(&default_debug_level, _ctx->debug);
+#endif
usbi_dbg(usbi_default_context, "created default context");
}
*ctx = _ctx;
if (!usbi_fallback_context) {
+#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
+ if (usbi_atomic_load(&default_debug_level) == -1)
+ usbi_atomic_store(&default_debug_level, _ctx->debug);
+#endif
usbi_fallback_context = _ctx;
- usbi_warn(usbi_fallback_context, "installing new context as implicit default");
+ usbi_dbg(usbi_fallback_context, "installing new context as implicit default");
}
}
list_del(&_ctx->list);
usbi_mutex_static_unlock(&active_contexts_lock);
+ /* Exit hotplug before backend dependency */
+ usbi_hotplug_exit(_ctx);
+
if (usbi_backend.exit)
usbi_backend.exit(_ctx);
/* Don't bother with locking after this point because unless there is
* an application bug, nobody will be accessing the context. */
- usbi_hotplug_exit(_ctx);
usbi_io_exit(_ctx);
for_each_device(_ctx, dev) {
/** \ingroup libusb_misc
* Check at runtime if the loaded library has a given capability.
- * This call should be performed after \ref libusb_init(), to ensure the
+ * This call should be performed after \ref libusb_init_context(), to ensure the
* backend has updated its capability set.
*
* \param capability the \ref libusb_capability to check for
UNUSED(ctx);
#else
enum libusb_log_level ctx_level;
+ long default_level_value;
- ctx = ctx ? ctx : usbi_default_context;
- ctx = ctx ? ctx : usbi_fallback_context;
- if (ctx)
+ if (ctx) {
ctx_level = ctx->debug;
- else
- ctx_level = get_env_debug_level();
+ } else {
+ default_level_value = usbi_atomic_load(&default_debug_level);
+ ctx_level = default_level_value < 0 ? get_env_debug_level() : (enum libusb_log_level)default_level_value;
+ }
if (ctx_level < level)
return;
*
* This is a non-blocking function; the device descriptor is cached in memory.
*
- * Note since libusb-1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102, this
+ * Note since libusb-1.0.16, \ref LIBUSBX_API_VERSION >= 0x01000102, this
* function always succeeds.
*
* \param dev the device
* valid if 0 was returned. Must be freed with libusb_free_config_descriptor()
* after use.
* \returns 0 on success
- * \returns LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state
+ * \returns \ref LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state
* \returns another LIBUSB_ERROR code on error
* \see libusb_get_config_descriptor
*/
* valid if 0 was returned. Must be freed with libusb_free_config_descriptor()
* after use.
* \returns 0 on success
- * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist
+ * \returns \ref LIBUSB_ERROR_NOT_FOUND if the configuration does not exist
* \returns another LIBUSB_ERROR code on error
* \see libusb_get_active_config_descriptor()
* \see libusb_get_config_descriptor_by_value()
* valid if 0 was returned. Must be freed with libusb_free_config_descriptor()
* after use.
* \returns 0 on success
- * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist
+ * \returns \ref LIBUSB_ERROR_NOT_FOUND if the configuration does not exist
* \returns another LIBUSB_ERROR code on error
* \see libusb_get_active_config_descriptor()
* \see libusb_get_config_descriptor()
* descriptor. Only valid if 0 was returned. Must be freed with
* libusb_free_ss_endpoint_companion_descriptor() after use.
* \returns 0 on success
- * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist
+ * \returns \ref LIBUSB_ERROR_NOT_FOUND if the configuration does not exist
* \returns another LIBUSB_ERROR code on error
*/
int API_EXPORTED libusb_get_ss_endpoint_companion_descriptor(
* \param bos output location for the BOS descriptor. Only valid if 0 was returned.
* Must be freed with \ref libusb_free_bos_descriptor() after use.
* \returns 0 on success
- * \returns LIBUSB_ERROR_NOT_FOUND if the device doesn't have a BOS descriptor
+ * \returns \ref LIBUSB_ERROR_NOT_FOUND if the device doesn't have a BOS descriptor
* \returns another LIBUSB_ERROR code on error
*/
int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *dev_handle,
free(container_id);
}
+/** \ingroup libusb_desc
+ * Get a platform descriptor
+ *
+ * Since version 1.0.27, \ref LIBUSB_API_VERSION >= 0x0100010A
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param dev_cap Device Capability descriptor with a bDevCapabilityType of
+ * \ref libusb_capability_type::LIBUSB_BT_PLATFORM_DESCRIPTOR
+ * LIBUSB_BT_PLATFORM_DESCRIPTOR
+ * \param platform_descriptor output location for the Platform descriptor.
+ * Only valid if 0 was returned. Must be freed with
+ * libusb_free_platform_descriptor() after use.
+ * \returns 0 on success
+ * \returns a LIBUSB_ERROR code on error
+ */
+int API_EXPORTED libusb_get_platform_descriptor(libusb_context *ctx,
+ struct libusb_bos_dev_capability_descriptor *dev_cap,
+ struct libusb_platform_descriptor **platform_descriptor)
+{
+ struct libusb_platform_descriptor *_platform_descriptor;
+
+ if (dev_cap->bDevCapabilityType != LIBUSB_BT_PLATFORM_DESCRIPTOR) {
+ usbi_err(ctx, "unexpected bDevCapabilityType 0x%x (expected 0x%x)",
+ dev_cap->bDevCapabilityType,
+ LIBUSB_BT_PLATFORM_DESCRIPTOR);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ } else if (dev_cap->bLength < LIBUSB_BT_PLATFORM_DESCRIPTOR_MIN_SIZE) {
+ usbi_err(ctx, "short dev-cap descriptor read %u/%d",
+ dev_cap->bLength, LIBUSB_BT_PLATFORM_DESCRIPTOR_MIN_SIZE);
+ return LIBUSB_ERROR_IO;
+ }
+
+ _platform_descriptor = malloc(dev_cap->bLength);
+ if (!_platform_descriptor)
+ return LIBUSB_ERROR_NO_MEM;
+
+ parse_descriptor(dev_cap, "bbbbu", _platform_descriptor);
+
+ /* Capability data is located after reserved byte and 128-bit UUID */
+ uint8_t* capability_data = dev_cap->dev_capability_data + 1 + 16;
+
+ /* Capability data length is total descriptor length minus initial fields */
+ size_t capability_data_length = _platform_descriptor->bLength - (16 + 4);
+
+ memcpy(_platform_descriptor->CapabilityData, capability_data, capability_data_length);
+
+ *platform_descriptor = _platform_descriptor;
+ return LIBUSB_SUCCESS;
+}
+
+/** \ingroup libusb_desc
+ * Free a platform descriptor obtained from
+ * libusb_get_platform_descriptor().
+ * It is safe to call this function with a NULL platform_descriptor parameter,
+ * in which case the function simply returns.
+ *
+ * \param platform_descriptor the Platform descriptor to free
+ */
+void API_EXPORTED libusb_free_platform_descriptor(
+ struct libusb_platform_descriptor *platform_descriptor)
+{
+ free(platform_descriptor);
+}
+
/** \ingroup libusb_desc
* Retrieve a string descriptor in C style ASCII.
*
uint8_t desc_index, unsigned char *data, int length)
{
union usbi_string_desc_buf str;
- int r, si, di;
+ int r;
uint16_t langid, wdata;
/* Asking for the zero'th index is special - it returns a string
else if ((str.desc.bLength & 1) || str.desc.bLength != r)
usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor (read %d)", str.desc.bLength, r);
- di = 0;
- for (si = 2; si < str.desc.bLength; si += 2) {
- if (di >= (length - 1))
- break;
+ /* Stop one byte before the end to leave room for null termination. */
+ int dest_max = length - 1;
+
+ /* The descriptor has this number of wide characters */
+ int src_max = (str.desc.bLength - 1 - 1) / 2;
- wdata = libusb_le16_to_cpu(str.desc.wData[di]);
+ /* Neither read nor write more than the smallest buffer */
+ int idx_max = MIN(dest_max, src_max);
+
+ int idx;
+ for (idx = 0; idx < idx_max; ++idx) {
+ wdata = libusb_le16_to_cpu(str.desc.wData[idx]);
if (wdata < 0x80)
- data[di++] = (unsigned char)wdata;
+ data[idx] = (unsigned char)wdata;
else
- data[di++] = '?'; /* non-ASCII */
+ data[idx] = '?'; /* non-ASCII */
+ }
+
+ data[idx] = 0; /* null-terminate string */
+ return idx;
+}
+
+static int parse_iad_array(struct libusb_context *ctx,
+ struct libusb_interface_association_descriptor_array *iad_array,
+ const uint8_t *buffer, int size)
+{
+ uint8_t i;
+ struct usbi_descriptor_header header;
+ int consumed = 0;
+ const uint8_t *buf = buffer;
+ struct libusb_interface_association_descriptor *iad;
+
+ if (size < LIBUSB_DT_CONFIG_SIZE) {
+ usbi_err(ctx, "short config descriptor read %d/%d",
+ size, LIBUSB_DT_CONFIG_SIZE);
+ return LIBUSB_ERROR_IO;
+ }
+
+ // First pass: Iterate through desc list, count number of IADs
+ iad_array->length = 0;
+ while (consumed < size) {
+ parse_descriptor(buf, "bb", &header);
+ if (header.bLength < 2) {
+ usbi_err(ctx, "invalid descriptor bLength %d",
+ header.bLength);
+ return LIBUSB_ERROR_IO;
+ }
+ if (header.bDescriptorType == LIBUSB_DT_INTERFACE_ASSOCIATION)
+ iad_array->length++;
+ buf += header.bLength;
+ consumed += header.bLength;
+ }
+
+ iad_array->iad = NULL;
+ if (iad_array->length > 0) {
+ iad = calloc(iad_array->length, sizeof(*iad));
+ if (!iad)
+ return LIBUSB_ERROR_NO_MEM;
+
+ iad_array->iad = iad;
+
+ // Second pass: Iterate through desc list, fill IAD structures
+ consumed = 0;
+ i = 0;
+ while (consumed < size) {
+ parse_descriptor(buffer, "bb", &header);
+ if (header.bDescriptorType == LIBUSB_DT_INTERFACE_ASSOCIATION)
+ parse_descriptor(buffer, "bbbbbbbb", &iad[i++]);
+ buffer += header.bLength;
+ consumed += header.bLength;
+ }
}
- data[di] = 0;
- return di;
+ return LIBUSB_SUCCESS;
+}
+
+static int raw_desc_to_iad_array(struct libusb_context *ctx, const uint8_t *buf,
+ int size, struct libusb_interface_association_descriptor_array **iad_array)
+{
+ struct libusb_interface_association_descriptor_array *_iad_array
+ = calloc(1, sizeof(*_iad_array));
+ int r;
+
+ if (!_iad_array)
+ return LIBUSB_ERROR_NO_MEM;
+
+ r = parse_iad_array(ctx, _iad_array, buf, size);
+ if (r < 0) {
+ usbi_err(ctx, "parse_iad_array failed with error %d", r);
+ free(_iad_array);
+ return r;
+ }
+
+ *iad_array = _iad_array;
+ return LIBUSB_SUCCESS;
+}
+
+/** \ingroup libusb_desc
+ * Get an array of interface association descriptors (IAD) for a given
+ * configuration.
+ * This is a non-blocking function which does not involve any requests being
+ * sent to the device.
+ *
+ * \param dev a device
+ * \param config_index the index of the configuration you wish to retrieve the
+ * IADs for.
+ * \param iad_array output location for the array of IADs. Only valid if 0 was
+ * returned. Must be freed with libusb_free_interface_association_descriptors()
+ * after use. It's possible that a given configuration contains no IADs. In this
+ * case the iad_array is still output, but will have 'length' field set to 0, and
+ * iad field set to NULL.
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist
+ * \returns another LIBUSB_ERROR code on error
+ * \see libusb_get_active_interface_association_descriptors()
+ */
+int API_EXPORTED libusb_get_interface_association_descriptors(libusb_device *dev,
+ uint8_t config_index, struct libusb_interface_association_descriptor_array **iad_array)
+{
+ union usbi_config_desc_buf _config;
+ uint16_t config_len;
+ uint8_t *buf;
+ int r;
+
+ if (!iad_array)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ usbi_dbg(DEVICE_CTX(dev), "IADs for config index %u", config_index);
+ if (config_index >= dev->device_descriptor.bNumConfigurations)
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ r = get_config_descriptor(dev, config_index, _config.buf, sizeof(_config.buf));
+ if (r < 0)
+ return r;
+
+ config_len = libusb_le16_to_cpu(_config.desc.wTotalLength);
+ buf = malloc(config_len);
+ if (!buf)
+ return LIBUSB_ERROR_NO_MEM;
+
+ r = get_config_descriptor(dev, config_index, buf, config_len);
+ if (r >= 0)
+ r = raw_desc_to_iad_array(DEVICE_CTX(dev), buf, r, iad_array);
+
+ free(buf);
+ return r;
+}
+
+/** \ingroup libusb_desc
+ * Get an array of interface association descriptors (IAD) for the currently
+ * active configuration.
+ * This is a non-blocking function which does not involve any requests being
+ * sent to the device.
+ *
+ * \param dev a device
+ * \param iad_array output location for the array of IADs. Only valid if 0 was
+ * returned. Must be freed with libusb_free_interface_association_descriptors()
+ * after use. It's possible that a given configuration contains no IADs. In this
+ * case the iad_array is still output, but will have 'length' field set to 0, and
+ * iad field set to NULL.
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state
+ * \returns another LIBUSB_ERROR code on error
+ * \see libusb_get_interface_association_descriptors
+ */
+int API_EXPORTED libusb_get_active_interface_association_descriptors(libusb_device *dev,
+ struct libusb_interface_association_descriptor_array **iad_array)
+{
+ union usbi_config_desc_buf _config;
+ uint16_t config_len;
+ uint8_t *buf;
+ int r;
+
+ if (!iad_array)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ r = get_active_config_descriptor(dev, _config.buf, sizeof(_config.buf));
+ if (r < 0)
+ return r;
+
+ config_len = libusb_le16_to_cpu(_config.desc.wTotalLength);
+ buf = malloc(config_len);
+ if (!buf)
+ return LIBUSB_ERROR_NO_MEM;
+
+ r = get_active_config_descriptor(dev, buf, config_len);
+ if (r >= 0)
+ r = raw_desc_to_iad_array(DEVICE_CTX(dev), buf, r, iad_array);
+ free(buf);
+ return r;
+}
+
+/** \ingroup libusb_desc
+ * Free an array of interface association descriptors (IADs) obtained from
+ * libusb_get_interface_association_descriptors() or
+ * libusb_get_active_interface_association_descriptors().
+ * It is safe to call this function with a NULL iad_array parameter, in which
+ * case the function simply returns.
+ *
+ * \param iad_array the IAD array to free
+ */
+void API_EXPORTED libusb_free_interface_association_descriptors(
+ struct libusb_interface_association_descriptor_array *iad_array)
+{
+ if (!iad_array)
+ return;
+
+ if (iad_array->iad)
+ free((void*)iad_array->iad);
+ free(iad_array);
}
*
* \section hotplug_intro Introduction
*
- * Version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102, has added support
+ * Version 1.0.16, \ref LIBUSBX_API_VERSION >= 0x01000102, has added support
* for hotplug events on <b>some</b> platforms (you should test if your platform
* supports hotplug notification by calling \ref libusb_has_capability() with
* parameter \ref LIBUSB_CAP_HAS_HOTPLUG).
libusb_hotplug_callback_handle callback_handle;
int rc;
- libusb_init(NULL);
+ libusb_init_context(NULL, NULL, 0);
rc = libusb_hotplug_register_callback(NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, 0, 0x045a, 0x5005,
for_each_hotplug_cb_safe(ctx, hotplug_cb, next_cb) {
if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE) {
usbi_dbg(ctx, "freeing hotplug cb %p with handle %d",
- hotplug_cb, hotplug_cb->handle);
+ (void *) hotplug_cb, hotplug_cb->handle);
list_del(&hotplug_cb->list);
free(hotplug_cb);
}
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
- usbi_dbg(ctx, "new hotplug cb %p with handle %d", hotplug_cb, hotplug_cb->handle);
+ usbi_dbg(ctx, "new hotplug cb %p with handle %d",
+ (void *) hotplug_cb, hotplug_cb->handle);
if ((flags & LIBUSB_HOTPLUG_ENUMERATE) && (events & LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)) {
ssize_t i, len;
* I/O functions for libusb
* Copyright © 2007-2009 Daniel Drake <dsd@gentoo.org>
* Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com>
- * Copyright © 2019 Nathan Hjelm <hjelmn@cs.umm.edu>
- * Copyright © 2019 Google LLC. All rights reserved.
+ * Copyright © 2019-2022 Nathan Hjelm <hjelmn@cs.unm.edu>
+ * Copyright © 2019-2022 Google LLC. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* be invoked, and the callback function should check the transfer status to
* determine that it was cancelled.
*
+ * On macOS and iOS it is not possible to cancel a single transfer. In this
+ * case cancelling one transfer on an endpoint will cause all transfers on
+ * that endpoint to be cancelled.
+ *
* Freeing the transfer after it has been cancelled but before cancellation
* has completed will result in undefined behaviour.
*
static void calculate_timeout(struct usbi_transfer *itransfer)
{
- unsigned int timeout =
- USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout;
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ unsigned int timeout = transfer->timeout;
if (!timeout) {
TIMESPEC_CLEAR(&itransfer->timeout);
struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(
int iso_packets)
{
- size_t priv_size;
- size_t alloc_size;
- unsigned char *ptr;
- struct usbi_transfer *itransfer;
- struct libusb_transfer *transfer;
-
assert(iso_packets >= 0);
if (iso_packets < 0)
return NULL;
- priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size);
- alloc_size = priv_size
- + sizeof(struct usbi_transfer)
- + sizeof(struct libusb_transfer)
- + (sizeof(struct libusb_iso_packet_descriptor) * (size_t)iso_packets);
- ptr = calloc(1, alloc_size);
+ size_t priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size);
+ size_t usbi_transfer_size = PTR_ALIGN(sizeof(struct usbi_transfer));
+ size_t libusb_transfer_size = PTR_ALIGN(sizeof(struct libusb_transfer));
+ size_t iso_packets_size = sizeof(struct libusb_iso_packet_descriptor) * (size_t)iso_packets;
+ size_t alloc_size = priv_size + usbi_transfer_size + libusb_transfer_size + iso_packets_size;
+ unsigned char *ptr = calloc(1, alloc_size);
if (!ptr)
return NULL;
- itransfer = (struct usbi_transfer *)(ptr + priv_size);
+ struct usbi_transfer *itransfer = (struct usbi_transfer *)(ptr + priv_size);
itransfer->num_iso_packets = iso_packets;
itransfer->priv = ptr;
usbi_mutex_init(&itransfer->lock);
- transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
return transfer;
}
*/
void API_EXPORTED libusb_free_transfer(struct libusb_transfer *transfer)
{
- struct usbi_transfer *itransfer;
- size_t priv_size;
- unsigned char *ptr;
-
if (!transfer)
return;
- usbi_dbg(TRANSFER_CTX(transfer), "transfer %p", transfer);
+ usbi_dbg(TRANSFER_CTX(transfer), "transfer %p", (void *) transfer);
if (transfer->flags & LIBUSB_TRANSFER_FREE_BUFFER)
free(transfer->buffer);
- itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
+ struct usbi_transfer *itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
usbi_mutex_destroy(&itransfer->lock);
if (itransfer->dev)
libusb_unref_device(itransfer->dev);
- priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size);
- ptr = (unsigned char *)itransfer - priv_size;
+ unsigned char *ptr = USBI_TRANSFER_TO_TRANSFER_PRIV(itransfer);
assert(ptr == itransfer->priv);
free(ptr);
}
/* iterates through the flying transfers, and rearms the timer based on the
* next upcoming timeout.
- * must be called with flying_list locked.
+ * NB: flying_transfers_lock must be held when calling this.
* returns 0 on success or a LIBUSB_ERROR code on failure.
*/
#ifdef HAVE_OS_TIMER
/* act on first transfer that has not already been handled */
if (!(itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))) {
- usbi_dbg(ctx, "next timeout originally %ums", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout);
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ usbi_dbg(ctx, "next timeout originally %ums", transfer->timeout);
return usbi_arm_timer(&ctx->timer, cur_ts);
}
}
/* add a transfer to the (timeout-sorted) active transfers list.
* This function will return non 0 if fails to update the timer,
- * in which case the transfer is *not* on the flying_transfers list. */
+ * in which case the transfer is *not* on the flying_transfers list.
+ * NB: flying_transfers_lock MUST be held when calling this. */
static int add_to_flying_list(struct usbi_transfer *itransfer)
{
struct usbi_transfer *cur;
if (first && usbi_using_timer(ctx) && TIMESPEC_IS_SET(timeout)) {
/* if this transfer has the lowest timeout of all active transfers,
* rearm the timer with this transfer's timeout */
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
usbi_dbg(ctx, "arm timer for timeout in %ums (first in line)",
- USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout);
+ transfer->timeout);
r = usbi_arm_timer(&ctx->timer, timeout);
}
#else
/* remove a transfer from the active transfers list.
* This function will *always* remove the transfer from the
* flying_transfers list. It will return a LIBUSB_ERROR code
- * if it fails to update the timer for the next timeout. */
+ * if it fails to update the timer for the next timeout.
+ * NB: flying_transfers_lock MUST be held when calling this. */
static int remove_from_flying_list(struct usbi_transfer *itransfer)
{
struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
int rearm_timer;
int r = 0;
- usbi_mutex_lock(&ctx->flying_transfers_lock);
rearm_timer = (TIMESPEC_IS_SET(&itransfer->timeout) &&
list_first_entry(&ctx->flying_transfers, struct usbi_transfer, list) == itransfer);
list_del(&itransfer->list);
if (rearm_timer)
r = arm_timer_for_next_timeout(ctx);
- usbi_mutex_unlock(&ctx->flying_transfers_lock);
return r;
}
*
* \param transfer the transfer to submit
* \returns 0 on success
- * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
- * \returns LIBUSB_ERROR_BUSY if the transfer has already been submitted.
- * \returns LIBUSB_ERROR_NOT_SUPPORTED if the transfer flags are not supported
+ * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns \ref LIBUSB_ERROR_BUSY if the transfer has already been submitted.
+ * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED if the transfer flags are not supported
* by the operating system.
- * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than
+ * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than
* the operating system and/or hardware can support (see \ref asynclimits)
* \returns another LIBUSB_ERROR code on other failure
*/
itransfer->dev = libusb_ref_device(transfer->dev_handle->dev);
ctx = HANDLE_CTX(transfer->dev_handle);
- usbi_dbg(ctx, "transfer %p", transfer);
+ usbi_dbg(ctx, "transfer %p", (void *) transfer);
/*
* Important note on locking, this function takes / releases locks
}
usbi_mutex_unlock(&itransfer->lock);
- if (r != LIBUSB_SUCCESS)
+ if (r != LIBUSB_SUCCESS) {
+ usbi_mutex_lock(&ctx->flying_transfers_lock);
remove_from_flying_list(itransfer);
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
+ }
return r;
}
* \ref libusb_transfer_status::LIBUSB_TRANSFER_CANCELLED
* "LIBUSB_TRANSFER_CANCELLED" for each transfer that was cancelled.
- * - Calling this function also sends a \c ClearFeature(ENDPOINT_HALT) request
- * for the transfer's endpoint. If the device does not handle this request
- * correctly, the data toggle bits for the endpoint can be left out of sync
- * between host and device, which can have unpredictable results when the
- * next data is sent on the endpoint, including data being silently lost.
- * A call to \ref libusb_clear_halt will not resolve this situation, since
- * that function uses the same request. Therefore, if your program runs on
- * Darwin and uses a device that does not correctly implement
- * \c ClearFeature(ENDPOINT_HALT) requests, it may only be safe to cancel
- * transfers when followed by a device reset using
+ * - When built for macOS versions prior to 10.5, this function sends a
+ * \c ClearFeature(ENDPOINT_HALT) request for the transfer's endpoint.
+ * (Prior to libusb 1.0.27, this request was sent on all Darwin systems.)
+ * If the device does not handle this request correctly, the data toggle
+ * bits for the endpoint can be left out of sync between host and device,
+ * which can have unpredictable results when the next data is sent on
+ * the endpoint, including data being silently lost. A call to
+ * \ref libusb_clear_halt will not resolve this situation, since that
+ * function uses the same request. Therefore, if your program runs on
+ * macOS < 10.5 (or libusb < 1.0.27), and uses a device that does not
+ * correctly implement \c ClearFeature(ENDPOINT_HALT) requests, it may
+ * only be safe to cancel transfers when followed by a device reset using
* \ref libusb_reset_device.
*
* \param transfer the transfer to cancel
* \returns 0 on success
- * \returns LIBUSB_ERROR_NOT_FOUND if the transfer is not in progress,
+ * \returns \ref LIBUSB_ERROR_NOT_FOUND if the transfer is not in progress,
* already complete, or already cancelled.
* \returns a LIBUSB_ERROR code on failure
*/
struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
int r;
- usbi_dbg(ctx, "transfer %p", transfer );
+ usbi_dbg(ctx, "transfer %p", (void *) transfer );
usbi_mutex_lock(&itransfer->lock);
if (!(itransfer->state_flags & USBI_TRANSFER_IN_FLIGHT)
|| (itransfer->state_flags & USBI_TRANSFER_CANCELLING)) {
uint8_t flags;
int r;
+ usbi_mutex_lock(&ctx->flying_transfers_lock);
r = remove_from_flying_list(itransfer);
+ usbi_mutex_unlock(&ctx->flying_transfers_lock);
if (r < 0)
usbi_err(ctx, "failed to set timer for next timeout");
flags = transfer->flags;
transfer->status = status;
transfer->actual_length = itransfer->transferred;
- usbi_dbg(ctx, "transfer %p has callback %p", transfer, transfer->callback);
- if (transfer->callback)
+ usbi_dbg(ctx, "transfer %p has callback %p",
+ (void *) transfer, transfer->callback);
+ if (transfer->callback) {
+ libusb_lock_event_waiters (ctx);
transfer->callback(transfer);
+ libusb_unlock_event_waiters(ctx);
+ }
/* transfer might have been freed by the above call, do not use from
* this point. */
if (flags & LIBUSB_TRANSFER_FREE_TRANSFER)
* indicates unlimited timeout.
* \returns 0 after a transfer completes or another thread stops event handling
* \returns 1 if the timeout expired
- * \returns LIBUSB_ERROR_INVALID_PARAM if timeval is invalid
+ * \returns \ref LIBUSB_ERROR_INVALID_PARAM if timeval is invalid
* \ref libusb_mtasync
*/
int API_EXPORTED libusb_wait_for_event(libusb_context *ctx, struct timeval *tv)
return 0;
}
+// NB: flying_transfers_lock must be held when calling this
static void handle_timeout(struct usbi_transfer *itransfer)
{
struct libusb_transfer *transfer =
"async cancel failed %d", r);
}
+// NB: flying_transfers_lock must be held when calling this
static void handle_timeouts_locked(struct libusb_context *ctx)
{
struct timespec systime;
* timeval struct for non-blocking mode
* \param completed pointer to completion integer to check, or NULL
* \returns 0 on success
- * \returns LIBUSB_ERROR_INVALID_PARAM if timeval is invalid
+ * \returns \ref LIBUSB_ERROR_INVALID_PARAM if timeval is invalid
* \returns another LIBUSB_ERROR code on other failure
* \ref libusb_mtasync
*/
* \param tv the maximum time to block waiting for events, or zero for
* non-blocking mode
* \returns 0 on success
- * \returns LIBUSB_ERROR_INVALID_PARAM if timeval is invalid
+ * \returns \ref LIBUSB_ERROR_INVALID_PARAM if timeval is invalid
* \returns another LIBUSB_ERROR code on other failure
* \ref libusb_mtasync
*/
* \param tv output location for a relative time against the current
* clock in which libusb must be called into in order to process timeout events
* \returns 0 if there are no pending timeouts, 1 if a timeout was returned,
- * or LIBUSB_ERROR_OTHER on failure
+ * or \ref LIBUSB_ERROR_OTHER on failure
*/
int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx,
struct timeval *tv)
* To remove notifiers, pass NULL values for the function pointers.
*
* Note that file descriptors may have been added even before you register
- * these notifiers (e.g. at libusb_init() time).
+ * these notifiers (e.g. at libusb_init_context() time).
*
* Additionally, note that the removal notifier may be called during
* libusb_exit() (e.g. when it is closing file descriptors that were opened
- * and added to the poll set at libusb_init() time). If you don't want this,
+ * and added to the poll set at libusb_init_context() time). If you don't want this,
* remove the notifiers immediately before calling libusb_exit().
*
* \param ctx the context to operate on, or NULL for the default context
to_cancel = NULL;
usbi_mutex_lock(&ctx->flying_transfers_lock);
for_each_transfer(ctx, cur) {
- if (USBI_TRANSFER_TO_LIBUSB_TRANSFER(cur)->dev_handle == dev_handle) {
+ struct libusb_transfer *cur_transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(cur);
+ if (cur_transfer->dev_handle == dev_handle) {
usbi_mutex_lock(&cur->lock);
if (cur->state_flags & USBI_TRANSFER_IN_FLIGHT)
to_cancel = cur;
if (!to_cancel)
break;
+ struct libusb_transfer *transfer_to_cancel = USBI_TRANSFER_TO_LIBUSB_TRANSFER(to_cancel);
usbi_dbg(ctx, "cancelling transfer %p from disconnect",
- USBI_TRANSFER_TO_LIBUSB_TRANSFER(to_cancel));
+ (void *) transfer_to_cancel);
usbi_mutex_lock(&to_cancel->lock);
usbi_backend.clear_transfer_priv(to_cancel);
* Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com>
* Copyright © 2007-2008 Daniel Drake <dsd@gentoo.org>
* Copyright © 2012 Pete Batard <pete@akeo.ie>
- * Copyright © 2012-2018 Nathan Hjelm <hjelmn@cs.unm.edu>
+ * Copyright © 2012-2023 Nathan Hjelm <hjelmn@cs.unm.edu>
* Copyright © 2014-2020 Chris Dickens <christopher.a.dickens@gmail.com>
- * For more information, please visit: http://libusb.info
+ * For more information, please visit: https://libusb.info
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include <time.h>
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
-#define ZERO_SIZED_ARRAY /* [] - valid C99 code */
+#define LIBUSB_FLEXIBLE_ARRAY /* [] - valid C99 code */
#else
-#define ZERO_SIZED_ARRAY 0 /* [0] - non-standard, but usually working code */
+#define LIBUSB_FLEXIBLE_ARRAY 0 /* [0] - non-standard, but usually working code */
#endif /* __STDC_VERSION__ */
/* 'interface' might be defined as a macro on Windows, so we need to
#define LIBUSB_DEPRECATED_FOR(f) __attribute__ ((deprecated ("Use " #f " instead")))
#elif defined(__GNUC__) && (__GNUC__ >= 3)
#define LIBUSB_DEPRECATED_FOR(f) __attribute__ ((deprecated))
+#elif defined(_MSC_VER)
+#define LIBUSB_DEPRECATED_FOR(f) __declspec(deprecated("Use " #f " instead"))
#else
#define LIBUSB_DEPRECATED_FOR(f)
#endif /* __GNUC__ */
*/
#if defined(_WIN32) || defined(__CYGWIN__)
#define LIBUSB_CALL WINAPI
+#define LIBUSB_CALLV WINAPIV
#else
#define LIBUSB_CALL
+#define LIBUSB_CALLV
#endif /* _WIN32 || __CYGWIN__ */
/** \def LIBUSB_API_VERSION
* \ingroup libusb_misc
* libusb's API version.
*
- * Since version 1.0.13, to help with feature detection, libusb defines
+ * Since version 1.0.18, to help with feature detection, libusb defines
* a LIBUSB_API_VERSION macro that gets increased every time there is a
* significant change to the API, such as the introduction of a new call,
* the definition of a new macro/enum member, or any other element that
* libusb applications may want to detect at compilation time.
*
+ * Between versions 1.0.13 and 1.0.17 (inclusive) the older spelling of
+ * LIBUSBX_API_VERSION was used.
+ *
* The macro is typically used in an application as follows:
* \code
* #if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01001234)
*
* Internally, LIBUSB_API_VERSION is defined as follows:
* (libusb major << 24) | (libusb minor << 16) | (16 bit incremental)
+ *
+ * The incremental component has changed as follows:
+ * <ul>
+ * <li>libusbx version 1.0.13: LIBUSBX_API_VERSION = 0x01000100
+ * <li>libusbx version 1.0.14: LIBUSBX_API_VERSION = 0x010000FF
+ * <li>libusbx version 1.0.15: LIBUSBX_API_VERSION = 0x01000101
+ * <li>libusbx version 1.0.16: LIBUSBX_API_VERSION = 0x01000102
+ * <li>libusbx version 1.0.17: LIBUSBX_API_VERSION = 0x01000102
+ * <li>libusb version 1.0.18: LIBUSB_API_VERSION = 0x01000102
+ * <li>libusb version 1.0.19: LIBUSB_API_VERSION = 0x01000103
+ * <li>libusb version 1.0.20: LIBUSB_API_VERSION = 0x01000104
+ * <li>libusb version 1.0.21: LIBUSB_API_VERSION = 0x01000105
+ * <li>libusb version 1.0.22: LIBUSB_API_VERSION = 0x01000106
+ * <li>libusb version 1.0.23: LIBUSB_API_VERSION = 0x01000107
+ * <li>libusb version 1.0.24: LIBUSB_API_VERSION = 0x01000108
+ * <li>libusb version 1.0.25: LIBUSB_API_VERSION = 0x01000109
+ * <li>libusb version 1.0.26: LIBUSB_API_VERSION = 0x01000109
+ * <li>libusb version 1.0.27: LIBUSB_API_VERSION = 0x0100010A
+ * </ul>
*/
-#define LIBUSB_API_VERSION 0x01000109
+#define LIBUSB_API_VERSION 0x0100010A
-/* The following is kept for compatibility, but will be deprecated in the future */
+/** \def LIBUSBX_API_VERSION
+ * \ingroup libusb_misc
+ *
+ * This is the older spelling, kept for backwards compatibility of code
+ * needing to test for older library versions where the newer spelling
+ * did not exist. */
#define LIBUSBX_API_VERSION LIBUSB_API_VERSION
#if defined(__cplusplus)
/** Endpoint descriptor. See libusb_endpoint_descriptor. */
LIBUSB_DT_ENDPOINT = 0x05,
+ /** Interface Association Descriptor.
+ * See libusb_interface_association_descriptor */
+ LIBUSB_DT_INTERFACE_ASSOCIATION = 0x0b,
+
/** BOS descriptor */
LIBUSB_DT_BOS = 0x0f,
#define LIBUSB_BT_USB_2_0_EXTENSION_SIZE 7
#define LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE 10
#define LIBUSB_BT_CONTAINER_ID_SIZE 20
+#define LIBUSB_BT_PLATFORM_DESCRIPTOR_MIN_SIZE 20
/* We unwrap the BOS => define its max size */
#define LIBUSB_DT_BOS_MAX_SIZE \
LIBUSB_BT_SS_USB_DEVICE_CAPABILITY = 0x03,
/** Container ID type */
- LIBUSB_BT_CONTAINER_ID = 0x04
+ LIBUSB_BT_CONTAINER_ID = 0x04,
+
+ /** Platform descriptor */
+ LIBUSB_BT_PLATFORM_DESCRIPTOR = 0x05
};
/** \ingroup libusb_desc
int extra_length;
};
+/** \ingroup libusb_desc
+ * A structure representing the standard USB interface association descriptor.
+ * This descriptor is documented in section 9.6.4 of the USB 3.0 specification.
+ * All multiple-byte fields are represented in host-endian format.
+ */
+struct libusb_interface_association_descriptor {
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_INTERFACE_ASSOCIATION
+ * LIBUSB_DT_INTERFACE_ASSOCIATION in this context. */
+ uint8_t bDescriptorType;
+
+ /** Interface number of the first interface that is associated
+ * with this function */
+ uint8_t bFirstInterface;
+
+ /** Number of contiguous interfaces that are associated with
+ * this function */
+ uint8_t bInterfaceCount;
+
+ /** USB-IF class code for this function.
+ * A value of zero is not allowed in this descriptor.
+ * If this field is 0xff, the function class is vendor-specific.
+ * All other values are reserved for assignment by the USB-IF.
+ */
+ uint8_t bFunctionClass;
+
+ /** USB-IF subclass code for this function.
+ * If this field is not set to 0xff, all values are reserved
+ * for assignment by the USB-IF
+ */
+ uint8_t bFunctionSubClass;
+
+ /** USB-IF protocol code for this function.
+ * These codes are qualified by the values of the bFunctionClass
+ * and bFunctionSubClass fields.
+ */
+ uint8_t bFunctionProtocol;
+
+ /** Index of string descriptor describing this function */
+ uint8_t iFunction;
+};
+
+/** \ingroup libusb_desc
+ * Structure containing an array of 0 or more interface association
+ * descriptors
+ */
+struct libusb_interface_association_descriptor_array {
+ /** Array of interface association descriptors. The size of this array
+ * is determined by the length field.
+ */
+ const struct libusb_interface_association_descriptor *iad;
+
+ /** Number of interface association descriptors contained. Read-only. */
+ int length;
+};
+
/** \ingroup libusb_desc
* A structure representing the standard USB interface descriptor. This
* descriptor is documented in section 9.6.5 of the USB 3.0 specification.
uint8_t bDevCapabilityType;
/** Device Capability data (bLength - 3 bytes) */
- uint8_t dev_capability_data[ZERO_SIZED_ARRAY];
+ uint8_t dev_capability_data[LIBUSB_FLEXIBLE_ARRAY];
};
/** \ingroup libusb_desc
uint8_t bNumDeviceCaps;
/** bNumDeviceCap Device Capability Descriptors */
- struct libusb_bos_dev_capability_descriptor *dev_capability[ZERO_SIZED_ARRAY];
+ struct libusb_bos_dev_capability_descriptor *dev_capability[LIBUSB_FLEXIBLE_ARRAY];
};
/** \ingroup libusb_desc
uint8_t ContainerID[16];
};
+/** \ingroup libusb_desc
+ * A structure representing a Platform descriptor.
+ * This descriptor is documented in section 9.6.2.4 of the USB 3.2 specification.
+ */
+struct libusb_platform_descriptor {
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY
+ * LIBUSB_DT_DEVICE_CAPABILITY in this context. */
+ uint8_t bDescriptorType;
+
+ /** Capability type. Will have value
+ * \ref libusb_capability_type::LIBUSB_BT_PLATFORM_DESCRIPTOR
+ * LIBUSB_BT_CONTAINER_ID in this context. */
+ uint8_t bDevCapabilityType;
+
+ /** Reserved field */
+ uint8_t bReserved;
+
+ /** 128 bit UUID */
+ uint8_t PlatformCapabilityUUID[16];
+
+ /** Capability data (bLength - 20) */
+ uint8_t CapabilityData[LIBUSB_FLEXIBLE_ARRAY];
+};
+
/** \ingroup libusb_asyncio
* Setup packet for control transfers. */
#if defined(_MSC_VER) || defined(__WATCOMC__)
* libusb_exit() will not destroy resources that the other user is still
* using.
*
- * Sessions are created by libusb_init() and destroyed through libusb_exit().
+ * Sessions are created by libusb_init_context() and destroyed through libusb_exit().
* If your application is guaranteed to only ever include a single libusb
* user (i.e. you), you do not have to worry about contexts: pass NULL in
* every function call where a context is required, and the default context
*
* This flag is currently only supported on Linux.
* On other systems, libusb_submit_transfer() will return
- * LIBUSB_ERROR_NOT_SUPPORTED for every transfer where this flag is set.
+ * \ref LIBUSB_ERROR_NOT_SUPPORTED for every transfer where this
+ * flag is set.
*
* Available since libusb-1.0.9.
*/
int num_iso_packets;
/** Isochronous packet descriptors, for isochronous transfers only. */
- struct libusb_iso_packet_descriptor iso_packet_desc[ZERO_SIZED_ARRAY];
+ struct libusb_iso_packet_descriptor iso_packet_desc[LIBUSB_FLEXIBLE_ARRAY];
};
/** \ingroup libusb_misc
LIBUSB_LOG_CB_CONTEXT = (1 << 1)
};
+/** \ingroup libusb_lib
+ * Available option values for libusb_set_option() and libusb_init_context().
+ */
+enum libusb_option {
+ /** Set the log message verbosity.
+ *
+ * This option must be provided an argument of type \ref libusb_log_level.
+ * The default level is LIBUSB_LOG_LEVEL_NONE, which means no messages are ever
+ * printed. If you choose to increase the message verbosity level, ensure
+ * that your application does not close the stderr file descriptor.
+ *
+ * You are advised to use level LIBUSB_LOG_LEVEL_WARNING. libusb is conservative
+ * with its message logging and most of the time, will only log messages that
+ * explain error conditions and other oddities. This will help you debug
+ * your software.
+ *
+ * If the LIBUSB_DEBUG environment variable was set when libusb was
+ * initialized, this option does nothing: the message verbosity is fixed
+ * to the value in the environment variable.
+ *
+ * If libusb was compiled without any message logging, this option does
+ * nothing: you'll never get any messages.
+ *
+ * If libusb was compiled with verbose debug message logging, this option
+ * does nothing: you'll always get messages from all levels.
+ */
+ LIBUSB_OPTION_LOG_LEVEL = 0,
+
+ /** Use the UsbDk backend for a specific context, if available.
+ *
+ * This option should be set at initialization with libusb_init_context()
+ * otherwise unspecified behavior may occur.
+ *
+ * Only valid on Windows. Ignored on all other platforms.
+ */
+ LIBUSB_OPTION_USE_USBDK = 1,
+
+ /** Do not scan for devices
+ *
+ * With this option set, libusb will skip scanning devices in
+ * libusb_init_context().
+ *
+ * Hotplug functionality will also be deactivated.
+ *
+ * The option is useful in combination with libusb_wrap_sys_device(),
+ * which can access a device directly without prior device scanning.
+ *
+ * This is typically needed on Android, where access to USB devices
+ * is limited.
+ *
+ * This option should only be used with libusb_init_context()
+ * otherwise unspecified behavior may occur.
+ *
+ * Only valid on Linux. Ignored on all other platforms.
+ */
+ LIBUSB_OPTION_NO_DEVICE_DISCOVERY = 2,
+
+#define LIBUSB_OPTION_WEAK_AUTHORITY LIBUSB_OPTION_NO_DEVICE_DISCOVERY
+
+ /** Set the context log callback function.
+ *
+ * Set the log callback function either on a context or globally. This
+ * option must be provided an argument of type \ref libusb_log_cb.
+ * Using this option with a NULL context is equivalent to calling
+ * libusb_set_log_cb() with mode \ref LIBUSB_LOG_CB_GLOBAL.
+ * Using it with a non-NULL context is equivalent to calling
+ * libusb_set_log_cb() with mode \ref LIBUSB_LOG_CB_CONTEXT.
+ */
+ LIBUSB_OPTION_LOG_CB = 3,
+
+ LIBUSB_OPTION_MAX = 4
+};
+
/** \ingroup libusb_lib
* Callback function for handling log messages.
* \param ctx the context which is related to the log message, or NULL if it
typedef void (LIBUSB_CALL *libusb_log_cb)(libusb_context *ctx,
enum libusb_log_level level, const char *str);
+/** \ingroup libusb_lib
+ * Structure used for setting options through \ref libusb_init_context.
+ *
+ */
+struct libusb_init_option {
+ /** Which option to set */
+ enum libusb_option option;
+ /** An integer value used by the option (if applicable). */
+ union {
+ int ival;
+ libusb_log_cb log_cbval;
+ } value;
+};
+
int LIBUSB_CALL libusb_init(libusb_context **ctx);
+int LIBUSB_CALL libusb_init_context(libusb_context **ctx, const struct libusb_init_option options[], int num_options);
void LIBUSB_CALL libusb_exit(libusb_context *ctx);
-LIBUSB_DEPRECATED_FOR(libusb_set_option)
void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level);
+/* may be deprecated in the future in favor of lubusb_init_context()+libusb_set_option() */
void LIBUSB_CALL libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb, int mode);
const struct libusb_version * LIBUSB_CALL libusb_get_version(void);
int LIBUSB_CALL libusb_has_capability(uint32_t capability);
struct libusb_container_id_descriptor **container_id);
void LIBUSB_CALL libusb_free_container_id_descriptor(
struct libusb_container_id_descriptor *container_id);
+int LIBUSB_CALL libusb_get_platform_descriptor(libusb_context *ctx,
+ struct libusb_bos_dev_capability_descriptor *dev_cap,
+ struct libusb_platform_descriptor **platform_descriptor);
+void LIBUSB_CALL libusb_free_platform_descriptor(
+ struct libusb_platform_descriptor *platform_descriptor);
uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev);
uint8_t LIBUSB_CALL libusb_get_port_number(libusb_device *dev);
int LIBUSB_CALL libusb_get_port_numbers(libusb_device *dev, uint8_t *port_numbers, int port_numbers_len);
unsigned char endpoint);
int LIBUSB_CALL libusb_get_max_iso_packet_size(libusb_device *dev,
unsigned char endpoint);
+int LIBUSB_CALL libusb_get_max_alt_packet_size(libusb_device *dev,
+ int interface_number, int alternate_setting, unsigned char endpoint);
+
+int LIBUSB_CALL libusb_get_interface_association_descriptors(libusb_device *dev,
+ uint8_t config_index, struct libusb_interface_association_descriptor_array **iad_array);
+int LIBUSB_CALL libusb_get_active_interface_association_descriptors(libusb_device *dev,
+ struct libusb_interface_association_descriptor_array **iad_array);
+void LIBUSB_CALL libusb_free_interface_association_descriptors(
+ struct libusb_interface_association_descriptor_array *iad_array);
int LIBUSB_CALL libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, libusb_device_handle **dev_handle);
int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **dev_handle);
* \param[in] cb_fn the function to be invoked on a matching event/device
* \param[in] user_data user data to pass to the callback function
* \param[out] callback_handle pointer to store the handle of the allocated callback (can be NULL)
- * \returns LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure
+ * \returns \ref LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure
*/
int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context *ctx,
int events, int flags,
void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx,
libusb_hotplug_callback_handle callback_handle);
-/** \ingroup libusb_lib
- * Available option values for libusb_set_option().
- */
-enum libusb_option {
- /** Set the log message verbosity.
- *
- * The default level is LIBUSB_LOG_LEVEL_NONE, which means no messages are ever
- * printed. If you choose to increase the message verbosity level, ensure
- * that your application does not close the stderr file descriptor.
- *
- * You are advised to use level LIBUSB_LOG_LEVEL_WARNING. libusb is conservative
- * with its message logging and most of the time, will only log messages that
- * explain error conditions and other oddities. This will help you debug
- * your software.
- *
- * If the LIBUSB_DEBUG environment variable was set when libusb was
- * initialized, this function does nothing: the message verbosity is fixed
- * to the value in the environment variable.
- *
- * If libusb was compiled without any message logging, this function does
- * nothing: you'll never get any messages.
- *
- * If libusb was compiled with verbose debug message logging, this function
- * does nothing: you'll always get messages from all levels.
- */
- LIBUSB_OPTION_LOG_LEVEL = 0,
-
- /** Use the UsbDk backend for a specific context, if available.
- *
- * This option should be set immediately after calling libusb_init(), otherwise
- * unspecified behavior may occur.
- *
- * Only valid on Windows.
- */
- LIBUSB_OPTION_USE_USBDK = 1,
-
- /** Do not scan for devices
- *
- * With this option set, libusb will skip scanning devices in
- * libusb_init(). Must be set before calling libusb_init().
- *
- * Hotplug functionality will also be deactivated.
- *
- * The option is useful in combination with libusb_wrap_sys_device(),
- * which can access a device directly without prior device scanning.
- *
- * This is typically needed on Android, where access to USB devices
- * is limited.
- *
- * For LIBUSB_API_VERSION 0x01000108 it was called LIBUSB_OPTION_WEAK_AUTHORITY
- *
- * Only valid on Linux.
- */
- LIBUSB_OPTION_NO_DEVICE_DISCOVERY = 2,
-
-#define LIBUSB_OPTION_WEAK_AUTHORITY LIBUSB_OPTION_NO_DEVICE_DISCOVERY
-
- LIBUSB_OPTION_MAX = 3
-};
-
-int LIBUSB_CALL libusb_set_option(libusb_context *ctx, enum libusb_option option, ...);
+int LIBUSB_CALLV libusb_set_option(libusb_context *ctx, enum libusb_option option, ...);
#ifdef _MSC_VER
#pragma warning(pop)
#endif
/* The following is used to silence warnings for unused variables */
-#if defined(UNREFERENCED_PARAMETER)
+#if defined(UNREFERENCED_PARAMETER) && !defined(__GNUC__)
#define UNUSED(var) UNREFERENCED_PARAMETER(var)
#else
#define UNUSED(var) do { (void)(var); } while(0)
* return_type LIBUSB_CALL function_name(params);
*/
#define API_EXPORTED LIBUSB_CALL DEFAULT_VISIBILITY
+#define API_EXPORTEDV LIBUSB_CALLV DEFAULT_VISIBILITY
#ifdef __cplusplus
extern "C" {
#else /* ENABLE_LOGGING */
-#define usbi_err(ctx, ...) UNUSED(ctx)
-#define usbi_warn(ctx, ...) UNUSED(ctx)
-#define usbi_info(ctx, ...) UNUSED(ctx)
-#define usbi_dbg(ctx, ...) do {} while (0)
+#define usbi_err(ctx, ...) do { (void)(ctx); } while(0)
+#define usbi_warn(ctx, ...) do { (void)(ctx); } while(0)
+#define usbi_info(ctx, ...) do { (void)(ctx); } while(0)
+#define usbi_dbg(ctx, ...) do { (void)(ctx); } while(0)
#endif /* ENABLE_LOGGING */
struct list_head flying_transfers;
/* Note paths taking both this and usbi_transfer->lock must always
* take this lock first */
- usbi_mutex_t flying_transfers_lock;
+ usbi_mutex_t flying_transfers_lock; /* for flying_transfers and timeout_flags */
#if !defined(PLATFORM_WINDOWS)
/* user callbacks for pollfd changes */
desc->bcdDevice = libusb_le16_to_cpu(desc->bcdDevice);
}
-#ifdef HAVE_CLOCK_GETTIME
+#if defined(HAVE_CLOCK_GETTIME) && !defined(__APPLE__)
static inline void usbi_get_monotonic_time(struct timespec *tp)
{
ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, tp), 0);
* 2. struct usbi_transfer
* 3. struct libusb_transfer (which includes iso packets) [variable size]
*
- * from a libusb_transfer, you can get the usbi_transfer by rewinding the
- * appropriate number of bytes.
+ * You can convert between them with the macros:
+ * TRANSFER_PRIV_TO_USBI_TRANSFER
+ * USBI_TRANSFER_TO_TRANSFER_PRIV
+ * USBI_TRANSFER_TO_LIBUSB_TRANSFER
+ * LIBUSB_TRANSFER_TO_USBI_TRANSFER
*/
struct usbi_transfer {
int transferred;
uint32_t stream_id;
uint32_t state_flags; /* Protected by usbi_transfer->lock */
- uint32_t timeout_flags; /* Protected by the flying_stransfers_lock */
+ uint32_t timeout_flags; /* Protected by the flying_transfers_lock */
/* The device reference is held until destruction for logging
* even after dev_handle is set to NULL. */
USBI_TRANSFER_TIMED_OUT = 1U << 2,
};
+#define TRANSFER_PRIV_TO_USBI_TRANSFER(transfer_priv) \
+ ((struct usbi_transfer *) \
+ ((unsigned char *)(transfer_priv) \
+ + PTR_ALIGN(sizeof(*transfer_priv))))
+
+#define USBI_TRANSFER_TO_TRANSFER_PRIV(itransfer) \
+ ((unsigned char *) \
+ ((unsigned char *)(itransfer) \
+ - PTR_ALIGN(usbi_backend.transfer_priv_size)))
+
#define USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer) \
((struct libusb_transfer *) \
((unsigned char *)(itransfer) \
+ PTR_ALIGN(sizeof(struct usbi_transfer))))
+
#define LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer) \
((struct usbi_transfer *) \
((unsigned char *)(transfer) \
struct usbi_string_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
- uint16_t wData[ZERO_SIZED_ARRAY];
+ uint16_t wData[LIBUSB_FLEXIBLE_ARRAY];
} LIBUSB_PACKED;
struct usbi_bos_descriptor {
int is_set;
union {
int ival;
+ libusb_log_cb log_cbval;
} arg;
};
struct discovered_devs {
size_t len;
size_t capacity;
- struct libusb_device *devices[ZERO_SIZED_ARRAY];
+ struct libusb_device *devices[LIBUSB_FLEXIBLE_ARRAY];
};
struct discovered_devs *discovered_devs_append(
* claiming, no other drivers/applications can use the interface because
* we now "own" it.
*
+ * This function gets called with dev_handle->lock locked!
+ *
* Return:
* - 0 on success
* - LIBUSB_ERROR_NOT_FOUND if the interface does not exist
* You will only ever be asked to release an interface which was
* successfully claimed earlier.
*
+ * This function gets called with dev_handle->lock locked!
+ *
* Return:
* - 0 on success
* - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it
*
* This function must not block.
*
- * This function gets called with the flying_transfers_lock locked!
+ * This function gets called with itransfer->lock locked!
*
* Return:
* - 0 on success
* This function must not block. The transfer cancellation must complete
* later, resulting in a call to usbi_handle_transfer_cancellation()
* from the context of handle_events.
+ *
+ * This function gets called with itransfer->lock locked!
*/
int (*cancel_transfer)(struct usbi_transfer *itransfer);
/* -*- Mode: C; indent-tabs-mode:nil -*- */
/*
* darwin backend for libusb 1.0
- * Copyright © 2008-2021 Nathan Hjelm <hjelmn@cs.unm.edu>
- * Copyright © 2019-2021 Google LLC. All rights reserved.
+ * Copyright © 2008-2023 Nathan Hjelm <hjelmn@cs.unm.edu>
+ * Copyright © 2019-2023 Google LLC. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include <fcntl.h>
#include <sys/sysctl.h>
-#include <mach/clock.h>
-#include <mach/clock_types.h>
-#include <mach/mach_host.h>
-#include <mach/mach_port.h>
+#include <mach/mach_time.h>
/* Suppress warnings about the use of the deprecated objc_registerThreadWithCollector
* function. Its use is also conditionalized to only older deployment targets. */
static const mach_port_t darwin_default_master_port = 0;
/* async event thread */
+/* if both this mutex and darwin_cached_devices_mutex are to be acquired then
+ darwin_cached_devices_mutex must be acquired first. */
static pthread_mutex_t libusb_darwin_at_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t libusb_darwin_at_cond = PTHREAD_COND_INITIALIZER;
-#if !defined(HAVE_CLOCK_GETTIME)
-static clock_serv_t clock_realtime;
-static clock_serv_t clock_monotonic;
-#endif
-
#define LIBUSB_DARWIN_STARTUP_FAILURE ((CFRunLoopRef) -1)
static CFRunLoopRef libusb_darwin_acfl = NULL; /* event cf loop */
static CFRunLoopSourceRef libusb_darwin_acfls = NULL; /* shutdown signal for event cf loop */
-static usbi_mutex_t darwin_cached_devices_lock = PTHREAD_MUTEX_INITIALIZER;
+static usbi_mutex_t darwin_cached_devices_mutex = PTHREAD_MUTEX_INITIALIZER;
static struct list_head darwin_cached_devices;
static const char *darwin_device_class = "IOUSBDevice";
+uint32_t libusb_testonly_fake_running_version __attribute__ ((visibility ("hidden")));
+int libusb_testonly_using_running_interface_version __attribute__ ((visibility ("hidden")));
+int libusb_testonly_using_running_device_version __attribute__ ((visibility ("hidden")));
+bool libusb_testonly_clear_running_version_cache __attribute__ ((visibility ("hidden")));
+
#define DARWIN_CACHED_DEVICE(a) (((struct darwin_device_priv *)usbi_get_device_priv((a)))->dev)
/* async event thread */
static pthread_t libusb_darwin_at;
+/* protected by libusb_darwin_at_mutex */
+static bool libusb_darwin_at_started;
+
+static void darwin_exit(struct libusb_context *ctx);
static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, void *buffer, size_t len);
static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8_t iface);
static int darwin_release_interface(struct libusb_device_handle *dev_handle, uint8_t iface);
static enum libusb_error darwin_get_cached_device(struct libusb_context *ctx, io_service_t service, struct darwin_cached_device **cached_out,
UInt64 *old_session_id);
+struct darwin_iokit_interface {
+ uint32_t min_os_version;
+ uint32_t version;
+ CFUUIDRef interface_id;
+};
+
+static const struct darwin_iokit_interface *get_interface_interface(void) {
+ const struct darwin_iokit_interface interfaces[] = {
+#if defined(kIOUSBInterfaceInterfaceID800)
+ {
+ .min_os_version = 101200,
+ .version = 800,
+ .interface_id = kIOUSBInterfaceInterfaceID800,
+ },
+#endif
+#if defined(kIOUSBInterfaceInterfaceID700)
+ {
+ .min_os_version = 101000,
+ .version = 700,
+ .interface_id = kIOUSBInterfaceInterfaceID700,
+ },
+#endif
+#if defined(kIOUSBInterfaceInterfaceID650)
+ {
+ .min_os_version = 100900,
+ .version = 650,
+ .interface_id = kIOUSBInterfaceInterfaceID650
+ },
+#endif
+#if defined(kIOUSBInterfaceInterfaceID550)
+ {
+ .min_os_version = 100803,
+ .version = 550,
+ .interface_id = kIOUSBInterfaceInterfaceID550,
+ },
+#endif
+#if defined(kIOUSBInterfaceInterfaceID245)
+ {
+ .min_os_version = 100407,
+ .version = 245,
+ .interface_id = kIOUSBInterfaceInterfaceID245,
+ },
+#endif
+ {
+ .min_os_version = 100000,
+ .version = 220,
+ .interface_id = kIOUSBInterfaceInterfaceID220,
+ },
+ {
+ .version = 0,
+ },
+ };
+ static struct darwin_iokit_interface cached_interface = {.version = 0};
+ if (libusb_testonly_clear_running_version_cache) {
+ memset (&cached_interface, 0, sizeof (cached_interface));
+ }
+ if (0 == cached_interface.version) {
+ uint32_t os_version = get_running_version();
+ for (int i = 0 ; interfaces[i].version > 0 ; ++i) {
+ if (os_version >= interfaces[i].min_os_version && cached_interface.min_os_version < interfaces[i].min_os_version) {
+ cached_interface = interfaces[i];
+ }
+ }
+
+ libusb_testonly_using_running_interface_version = cached_interface.version;
+ }
+
+ return &cached_interface;
+}
+
+static CFUUIDRef get_interface_interface_id(void) {
+ return get_interface_interface()->interface_id;
+}
+
+static int get_interface_interface_version(void) {
+ return get_interface_interface()->version;
+}
+
+static const struct darwin_iokit_interface *get_device_interface(void) {
+ struct darwin_iokit_interface interfaces[] = {
+#if defined(kIOUSBDeviceInterfaceID650)
+ {
+ .min_os_version = 100900,
+ .version = 650,
+ .interface_id = kIOUSBDeviceInterfaceID650,
+ },
+#endif
+#if defined(kIOUSBDeviceInterfaceID500)
+ {
+ .min_os_version = 100703,
+ .version = 500,
+ .interface_id = kIOUSBDeviceInterfaceID500,
+ },
+#endif
+#if defined(kIOUSBDeviceInterfaceID320)
+ {
+ .min_os_version = 100504,
+ .version = 320,
+ .interface_id = kIOUSBDeviceInterfaceID320,
+ },
+#endif
+#if defined(kIOUSBDeviceInterfaceID300)
+ {
+ .min_os_version = 100500,
+ .version = 300,
+ .interface_id = kIOUSBDeviceInterfaceID300,
+ },
+#endif
+#if defined(kIOUSBDeviceInterfaceID245)
+ {
+ .min_os_version = 100407,
+ .version = 245,
+ .interface_id = kIOUSBDeviceInterfaceID245,
+ },
+#endif
+ {
+ .min_os_version = 100000,
+ .version = 197,
+ .interface_id = kIOUSBDeviceInterfaceID197,
+ },
+ {
+ .version = 0,
+ },
+ };
+ static struct darwin_iokit_interface cached_interface = {.version = 0};
+ if (libusb_testonly_clear_running_version_cache) {
+ memset (&cached_interface, 0, sizeof (cached_interface));
+ }
+ if (0 == cached_interface.version) {
+ uint32_t os_version = get_running_version();
+ for (int i = 0 ; interfaces[i].version > 0 ; ++i) {
+ if (os_version >= interfaces[i].min_os_version && cached_interface.min_os_version < interfaces[i].min_os_version) {
+ cached_interface = interfaces[i];
+ }
+ }
+ libusb_testonly_using_running_device_version = cached_interface.version;
+ }
+
+ return &cached_interface;
+}
+
+static CFUUIDRef get_device_interface_id(void) {
+ return get_device_interface()->interface_id;
+}
+
+static int get_device_interface_version(void) {
+ return get_device_interface()->version;
+}
+
+struct darwin_pipe_properties {
+ uint8_t number;
+ uint8_t direction;
+ uint8_t transfer_type;
+ uint16_t max_packet_size;
+ uint8_t interval;
+};
+typedef struct darwin_pipe_properties darwin_pipe_properties_t;
+
+static IOReturn darwin_get_pipe_properties(struct darwin_interface *cInterface, uint8_t pipe, darwin_pipe_properties_t *out) {
+ IOReturn kresult;
+
+#if (MAX_INTERFACE_VERSION >= 550)
+ if (get_interface_interface_version() >= 550) {
+ IOUSBEndpointProperties pipe_properties = {.bVersion = kUSBEndpointPropertiesVersion3};
+ kresult = (*IOINTERFACE_V(cInterface, 550))->GetPipePropertiesV3 (IOINTERFACE(cInterface), pipe, &pipe_properties);
+ if (kIOReturnSuccess == kresult) {
+ out->number = pipe_properties.bEndpointNumber;
+ out->direction = pipe_properties.bDirection;
+ out->transfer_type = pipe_properties.bTransferType;
+ out->max_packet_size = pipe_properties.wMaxPacketSize;
+ out->interval = pipe_properties.bInterval;
+ }
+ return kresult;
+ }
+#endif
+ return (*IOINTERFACE(cInterface))->GetPipeProperties(IOINTERFACE(cInterface), pipe, &out->direction,
+ &out->number, &out->transfer_type, &out->max_packet_size,
+ &out->interval);
+}
+
#if defined(ENABLE_LOGGING)
static const char *darwin_error_str (IOReturn result) {
static char string_buffer[50];
}
}
-/* this function must be called with the darwin_cached_devices_lock held */
+uint32_t get_running_version(void) {
+ if (libusb_testonly_fake_running_version > 0) {
+ return libusb_testonly_fake_running_version;
+ }
+
+ int ret;
+#if !defined(TARGET_OS_OSX) || TARGET_OS_OSX == 1
+ char os_version_string[64] = {'\0'};;
+ size_t os_version_string_len = sizeof(os_version_string) - 1;
+
+ /* newer versions of macOS provide a sysctl for the OS version but this is not useful for iOS without
+ * code detecting this is iOS and a mapping from iOS -> macOS version. it is still useful to have since
+ * it provides the exact macOS version instead of the approximate version (as below). */
+ ret = sysctlbyname("kern.osproductversion", os_version_string, &os_version_string_len, NULL, 0);
+ if (ret == 0) {
+ int major = 10, minor = 0, patch = 0;
+ ret = sscanf(os_version_string, "%i.%i.%i", &major, &minor, &patch);
+ if (ret < 2) {
+ usbi_err (NULL, "could not determine the running OS version, assuming 10.0, kern.osproductversion=%s", os_version_string);
+ return 100000;
+ }
+ return (major * 10000) + (minor * 100) + patch;
+ }
+#endif
+
+ char os_release_string[64] = {'\0'};
+ size_t os_release_string_len = sizeof(os_release_string) - 1;
+ /* if the version can not be detected libusb assumes 10.0 so ignore any error here */
+ ret = sysctlbyname("kern.osrelease", os_release_string, &os_release_string_len, NULL, 0);
+ if (ret != 0) {
+ usbi_err (NULL, "could not read kern.osrelease, errno=", errno);
+ return 100000;
+ }
+
+ int darwin_major = 1, darwin_minor = 0;
+ ret = sscanf(os_release_string, "%i.%i", &darwin_major, &darwin_minor);
+ if (ret < 1) {
+ usbi_err (NULL, "could not determine the running Darwin version, assuming 1.3 (OS X 10.0), kern.osrelease=%s", os_release_string);
+ return 100000;
+ }
+
+ int major = 10, minor = 0, patch = 0;
+
+ if (1 == darwin_major && darwin_minor < 4) {
+ /* 10.0.x */
+ } else if (darwin_major < 6) {
+ /* assume 10.1 for anything in this range */
+ minor = 1;
+ } else if (darwin_major < 20) {
+ /* from macOS 10.2 through 10.15 the minor version can be calculated from the darwin_major by subtracting 4 and
+ * the patch level almost always matches darwin_minor. when the darwin_minor does not match the OS X patch level
+ * it is usually because Apple did not change it in a particular point release. when darwin_minor is changed it
+ * always matches the OS X/macOS patch level. */
+ minor = darwin_major - 4;
+ patch = darwin_minor;
+ } else {
+ /* unlikely to be used as kern.osproductversion is available from 10.10 on */
+ major = darwin_major - 9;
+ minor = darwin_minor;
+ /* ignore the patch level in this range */
+ }
+
+ return (major * 10000) + (minor * 100) + patch;
+}
+
+/* this function must be called with the darwin_cached_devices_mutex held */
static void darwin_deref_cached_device(struct darwin_cached_device *cached_dev) {
cached_dev->refcount--;
/* free the device and remove it from the cache */
list_del(&cached_dev->list);
if (cached_dev->device) {
- (*(cached_dev->device))->Release(cached_dev->device);
+ (*cached_dev->device)->Release(cached_dev->device);
cached_dev->device = NULL;
}
IOObjectRelease (cached_dev->service);
return success;
}
-static usb_device_t **darwin_device_from_service (struct libusb_context *ctx, io_service_t service)
+static int darwin_device_from_service (struct libusb_context *ctx, io_service_t service, usb_device_t* device)
{
io_cf_plugin_ref_t *plugInInterface = NULL;
- usb_device_t **device;
IOReturn kresult;
SInt32 score;
+
const int max_retries = 5;
/* The IOCreatePlugInInterfaceForService function might consistently return
nanosleep(&(struct timespec){.tv_sec = 0, .tv_nsec = 1000}, NULL);
}
- if (kIOReturnSuccess != kresult || !plugInInterface) {
+ if (kIOReturnSuccess != kresult) {
usbi_dbg (ctx, "could not set up plugin for service: %s", darwin_error_str (kresult));
- return NULL;
+ return darwin_to_libusb(kresult);
+ }
+ if (!plugInInterface) {
+ usbi_dbg (ctx, "could not set up plugin for service");
+ return LIBUSB_ERROR_OTHER;
}
- (void)(*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(DeviceInterfaceID),
- (LPVOID)&device);
+ (void)(*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(get_device_interface_id()),
+ (LPVOID)device);
/* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */
(*plugInInterface)->Release (plugInInterface);
- return device;
+ return LIBUSB_SUCCESS;
}
static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) {
struct darwin_cached_device *old_device;
io_service_t device;
- UInt64 session, locationID;
- int ret;
usbi_mutex_lock(&active_contexts_lock);
bool is_reenumerating = false;
/* get the location from the i/o registry */
- ret = get_ioregistry_value_number (device, CFSTR("sessionID"), kCFNumberSInt64Type, &session);
+ UInt64 session = 0;
+ bool ret = get_ioregistry_value_number (device, CFSTR("sessionID"), kCFNumberSInt64Type, &session);
+ UInt32 locationID = 0;
(void) get_ioregistry_value_number (device, CFSTR("locationID"), kCFNumberSInt32Type, &locationID);
IOObjectRelease (device);
if (!ret)
/* we need to match darwin_ref_cached_device call made in darwin_get_cached_device function
otherwise no cached device will ever get freed */
- usbi_mutex_lock(&darwin_cached_devices_lock);
+ usbi_mutex_lock(&darwin_cached_devices_mutex);
list_for_each_entry(old_device, &darwin_cached_devices, list, struct darwin_cached_device) {
if (old_device->session == session) {
if (old_device->in_reenumerate) {
/* device is re-enumerating. do not dereference the device at this time. libusb_reset_device()
* will deref if needed. */
- usbi_dbg (NULL, "detected device detached due to re-enumeration. sessionID: 0x%" PRIx64 ", locationID: 0x%" PRIx64,
- session, locationID);
+ usbi_dbg (NULL, "detected device detached due to re-enumeration. sessionID: 0x%" PRIx64
+ ", locationID: 0x%" PRIx32, session, locationID);
/* the device object is no longer usable so go ahead and release it */
if (old_device->device) {
- (*(old_device->device))->Release(old_device->device);
+ (*old_device->device)->Release(old_device->device);
old_device->device = NULL;
}
}
}
- usbi_mutex_unlock(&darwin_cached_devices_lock);
+ usbi_mutex_unlock(&darwin_cached_devices_mutex);
if (is_reenumerating) {
continue;
}
}
static void *darwin_event_thread_main (void *arg0) {
+ UNUSED(arg0);
IOReturn kresult;
- struct libusb_context *ctx = (struct libusb_context *)arg0;
CFRunLoopRef runloop;
CFRunLoopSourceRef libusb_shutdown_cfsource;
CFRunLoopSourceContext libusb_shutdown_cfsourcectx;
io_iterator_t libusb_add_device_iterator;
/* ctx must only be used for logging during thread startup */
- usbi_dbg (ctx, "creating hotplug event source");
+ usbi_dbg (NULL, "creating hotplug event source");
runloop = CFRunLoopGetCurrent ();
CFRetain (runloop);
NULL, &libusb_rem_device_iterator);
if (kresult != kIOReturnSuccess) {
- usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult));
+ usbi_err (NULL, "could not add hotplug event source: %s", darwin_error_str (kresult));
CFRelease (libusb_shutdown_cfsource);
CFRelease (runloop);
darwin_fail_startup ();
NULL, &libusb_add_device_iterator);
if (kresult != kIOReturnSuccess) {
- usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult));
+ usbi_err (NULL, "could not add hotplug event source: %s", darwin_error_str (kresult));
CFRelease (libusb_shutdown_cfsource);
CFRelease (runloop);
darwin_fail_startup ();
darwin_clear_iterator (libusb_rem_device_iterator);
darwin_clear_iterator (libusb_add_device_iterator);
- usbi_dbg (ctx, "darwin event thread ready to receive events");
+ usbi_dbg (NULL, "darwin event thread ready to receive events");
/* signal the main thread that the hotplug runloop has been created. */
pthread_mutex_lock (&libusb_darwin_at_mutex);
pthread_exit (NULL);
}
-/* cleanup function to destroy cached devices */
+/* cleanup function to destroy cached devices. must be called with a lock on darwin_cached_devices_mutex */
static void darwin_cleanup_devices(void) {
struct darwin_cached_device *dev, *next;
list_for_each_entry_safe(dev, next, &darwin_cached_devices, list, struct darwin_cached_device) {
+ if (dev->refcount > 1) {
+ usbi_err(NULL, "device still referenced at libusb_exit");
+ }
darwin_deref_cached_device(dev);
}
}
-static int darwin_init(struct libusb_context *ctx) {
- bool first_init;
- int rc;
+/* must be called with a lock on darwin_cached_devices_mutex */
+static int darwin_first_time_init(void) {
+ if (NULL == darwin_cached_devices.next) {
+ list_init (&darwin_cached_devices);
+ }
- first_init = (1 == ++init_count);
+ /* cache the interface versions that will be used. as a sanity check verify
+ * that the interface versions are non-zero. */
+ const struct darwin_iokit_interface *interface_interface = get_interface_interface();
+ const struct darwin_iokit_interface *device_interface = get_device_interface();
+ if (0 == interface_interface->version || 0 == device_interface->version) {
+ usbi_err(NULL, "could not determine the device or interface interface to use with this version "
+ "of macOS (or MacOS X), current_running_version = %" PRIu32, get_running_version());
+ return LIBUSB_ERROR_OTHER;
+ }
- do {
- if (first_init) {
- if (NULL == darwin_cached_devices.next) {
- list_init (&darwin_cached_devices);
- }
- assert(list_empty(&darwin_cached_devices));
-#if !defined(HAVE_CLOCK_GETTIME)
- /* create the clocks that will be used if clock_gettime() is not available */
- host_name_port_t host_self;
-
- host_self = mach_host_self();
- host_get_clock_service(host_self, CALENDAR_CLOCK, &clock_realtime);
- host_get_clock_service(host_self, SYSTEM_CLOCK, &clock_monotonic);
- mach_port_deallocate(mach_task_self(), host_self);
-#endif
- }
+ if (!list_empty(&darwin_cached_devices)) {
+ usbi_err(NULL, "libusb_device reference not released on last exit. will not continue");
+ return LIBUSB_ERROR_OTHER;
+ }
- rc = darwin_scan_devices (ctx);
- if (LIBUSB_SUCCESS != rc)
- break;
+ int rc = pthread_create (&libusb_darwin_at, NULL, darwin_event_thread_main, NULL);
+ if (0 != rc) {
+ usbi_err (NULL, "could not create event thread, error %d", rc);
+ return LIBUSB_ERROR_OTHER;
+ }
- if (first_init) {
- rc = pthread_create (&libusb_darwin_at, NULL, darwin_event_thread_main, ctx);
- if (0 != rc) {
- usbi_err (ctx, "could not create event thread, error %d", rc);
- rc = LIBUSB_ERROR_OTHER;
- break;
- }
+ pthread_mutex_lock (&libusb_darwin_at_mutex);
+ libusb_darwin_at_started = true;
+ while (NULL == libusb_darwin_acfl) {
+ pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex);
+ }
- pthread_mutex_lock (&libusb_darwin_at_mutex);
- while (!libusb_darwin_acfl)
- pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex);
- if (libusb_darwin_acfl == LIBUSB_DARWIN_STARTUP_FAILURE) {
- libusb_darwin_acfl = NULL;
- rc = LIBUSB_ERROR_OTHER;
- }
- pthread_mutex_unlock (&libusb_darwin_at_mutex);
+ if (libusb_darwin_acfl == LIBUSB_DARWIN_STARTUP_FAILURE) {
+ libusb_darwin_acfl = NULL;
+ rc = LIBUSB_ERROR_OTHER;
+ }
+ pthread_mutex_unlock (&libusb_darwin_at_mutex);
- if (0 != rc)
- pthread_join (libusb_darwin_at, NULL);
+ return rc;
+}
+
+static int darwin_init_context(struct libusb_context *ctx) {
+ usbi_mutex_lock(&darwin_cached_devices_mutex);
+
+ bool first_init = (1 == ++init_count);
+
+ if (first_init) {
+ int rc = darwin_first_time_init();
+ if (LIBUSB_SUCCESS != rc) {
+ usbi_mutex_unlock(&darwin_cached_devices_mutex);
+ return rc;
}
- } while (0);
+ }
+ usbi_mutex_unlock(&darwin_cached_devices_mutex);
+
+ return darwin_scan_devices (ctx);
+}
+static int darwin_init(struct libusb_context *ctx) {
+ int rc = darwin_init_context(ctx);
if (LIBUSB_SUCCESS != rc) {
- if (first_init) {
- darwin_cleanup_devices ();
-#if !defined(HAVE_CLOCK_GETTIME)
- mach_port_deallocate(mach_task_self(), clock_realtime);
- mach_port_deallocate(mach_task_self(), clock_monotonic);
-#endif
- }
- --init_count;
+ /* clean up any allocated resources */
+ darwin_exit(ctx);
}
return rc;
static void darwin_exit (struct libusb_context *ctx) {
UNUSED(ctx);
+ usbi_mutex_lock(&darwin_cached_devices_mutex);
if (0 == --init_count) {
/* stop the event runloop and wait for the thread to terminate. */
pthread_mutex_lock (&libusb_darwin_at_mutex);
- CFRunLoopSourceSignal (libusb_darwin_acfls);
- CFRunLoopWakeUp (libusb_darwin_acfl);
- while (libusb_darwin_acfl)
- pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex);
+ if (NULL != libusb_darwin_acfls) {
+ CFRunLoopSourceSignal (libusb_darwin_acfls);
+ CFRunLoopWakeUp (libusb_darwin_acfl);
+ while (libusb_darwin_acfl)
+ pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex);
+ }
+
+ if (libusb_darwin_at_started) {
+ pthread_join (libusb_darwin_at, NULL);
+ libusb_darwin_at_started = false;
+ }
pthread_mutex_unlock (&libusb_darwin_at_mutex);
- pthread_join (libusb_darwin_at, NULL);
darwin_cleanup_devices ();
-
-#if !defined(HAVE_CLOCK_GETTIME)
- mach_port_deallocate(mach_task_self(), clock_realtime);
- mach_port_deallocate(mach_task_self(), clock_monotonic);
-#endif
}
+ usbi_mutex_unlock(&darwin_cached_devices_mutex);
}
static int get_configuration_index (struct libusb_device *dev, UInt8 config_value) {
IOReturn kresult;
/* is there a simpler way to determine the index? */
- kresult = (*(priv->device))->GetNumberOfConfigurations (priv->device, &numConfig);
+ kresult = (*priv->device)->GetNumberOfConfigurations (priv->device, &numConfig);
if (kresult != kIOReturnSuccess)
return darwin_to_libusb (kresult);
for (i = 0 ; i < numConfig ; i++) {
- (*(priv->device))->GetConfigurationDescriptorPtr (priv->device, i, &desc);
+ (*priv->device)->GetConfigurationDescriptorPtr (priv->device, i, &desc);
if (desc->bConfigurationValue == config_value)
return i;
/* check whether the os has configured the device */
static enum libusb_error darwin_check_configuration (struct libusb_context *ctx, struct darwin_cached_device *dev) {
- usb_device_t **darwin_device = dev->device;
+ usb_device_t darwin_device = dev->device;
IOUSBConfigurationDescriptorPtr configDesc;
IOUSBFindInterfaceRequest request;
request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
- kresult = (*(darwin_device))->CreateInterfaceIterator(darwin_device, &request, &interface_iterator);
+ kresult = (*darwin_device)->CreateInterfaceIterator(darwin_device, &request, &interface_iterator);
if (kresult != kIOReturnSuccess)
return darwin_to_libusb (kresult);
return LIBUSB_SUCCESS;
}
-static IOReturn darwin_request_descriptor (usb_device_t **device, UInt8 desc, UInt8 desc_index, void *buffer, size_t buffer_size) {
+static IOReturn darwin_request_descriptor (usb_device_t device, UInt8 desc, UInt8 desc_index, void *buffer, size_t buffer_size) {
IOUSBDevRequestTO req;
assert(buffer_size <= UINT16_MAX);
}
static enum libusb_error darwin_cache_device_descriptor (struct libusb_context *ctx, struct darwin_cached_device *dev) {
- usb_device_t **device = dev->device;
+ usb_device_t device = dev->device;
int retries = 1;
long delay = 30000; // microseconds
int unsuspended = 0, try_unsuspend = 1, try_reconfigure = 1;
if (kIOReturnSuccess != ret && is_open && try_unsuspend) {
/* device may be suspended. unsuspend it and try again */
-#if DeviceVersion >= 320
- UInt32 info = 0;
+#if MAX_DEVICE_VERSION >= 320
+ if (get_device_interface_version() >= 320) {
+ UInt32 info = 0;
- /* IOUSBFamily 320+ provides a way to detect device suspension but earlier versions do not */
- (void)(*device)->GetUSBDeviceInformation (device, &info);
+ /* IOUSBFamily 320+ provides a way to detect device suspension but earlier versions do not */
+ (void)(*IODEVICE_V(device, 320))->GetUSBDeviceInformation (device, &info);
- /* note that the device was suspended */
- if (info & (1U << kUSBInformationDeviceIsSuspendedBit) || 0 == info)
- try_unsuspend = 1;
+ /* note that the device was suspended */
+ if (info & (1U << kUSBInformationDeviceIsSuspendedBit) || 0 == info)
+ try_unsuspend = 1;
+ }
#endif
if (try_unsuspend) {
UInt64 sessionID = 0, parent_sessionID = 0;
UInt32 locationID = 0;
enum libusb_error ret = LIBUSB_SUCCESS;
- usb_device_t **device;
+ usb_device_t device;
UInt8 port = 0;
/* assuming sessionID != 0 normally (never seen it be 0) */
usbi_dbg(ctx, "parent sessionID: 0x%" PRIx64, parent_sessionID);
}
- usbi_mutex_lock(&darwin_cached_devices_lock);
+ usbi_mutex_lock(&darwin_cached_devices_mutex);
do {
list_for_each_entry(new_device, &darwin_cached_devices, list, struct darwin_cached_device) {
- usbi_dbg(ctx, "matching sessionID/locationID 0x%" PRIx64 "/0x%x against cached device with sessionID/locationID 0x%" PRIx64 "/0x%x",
+ usbi_dbg(ctx, "matching sessionID/locationID 0x%" PRIx64 "/0x%" PRIx32 " against cached device with sessionID/locationID 0x%" PRIx64 "/0x%" PRIx32,
sessionID, locationID, new_device->session, new_device->location);
if (new_device->location == locationID && new_device->in_reenumerate) {
usbi_dbg (ctx, "found cached device with matching location that is being re-enumerated");
usbi_dbg(ctx, "caching new device with sessionID 0x%" PRIx64, sessionID);
- device = darwin_device_from_service (ctx, service);
- if (!device) {
- ret = LIBUSB_ERROR_NO_DEVICE;
+ ret = darwin_device_from_service (ctx, service, &device);
+ if (LIBUSB_SUCCESS != ret) {
break;
}
}
} while (0);
- usbi_mutex_unlock(&darwin_cached_devices_lock);
+ usbi_mutex_unlock(&darwin_cached_devices_mutex);
return ret;
}
dev->parent_dev = usbi_get_device_by_session_id (ctx, (unsigned long) cached_device->parent_session);
}
- (*(priv->dev->device))->GetDeviceSpeed (priv->dev->device, &devSpeed);
+ (*priv->dev->device)->GetDeviceSpeed (priv->dev->device, &devSpeed);
switch (devSpeed) {
case kUSBDeviceSpeedLow: dev->speed = LIBUSB_SPEED_LOW; break;
if (0 == dpriv->open_count) {
/* try to open the device */
- kresult = (*(dpriv->device))->USBDeviceOpenSeize (dpriv->device);
+ kresult = (*dpriv->device)->USBDeviceOpenSeize (dpriv->device);
if (kresult != kIOReturnSuccess) {
usbi_warn (HANDLE_CTX (dev_handle), "USBDeviceOpen: %s", darwin_error_str(kresult));
}
/* create async event source */
- kresult = (*(dpriv->device))->CreateDeviceAsyncEventSource (dpriv->device, &priv->cfSource);
+ kresult = (*dpriv->device)->CreateDeviceAsyncEventSource (dpriv->device,
+ &priv->cfSource);
if (kresult != kIOReturnSuccess) {
usbi_err (HANDLE_CTX (dev_handle), "CreateDeviceAsyncEventSource: %s", darwin_error_str(kresult));
if (priv->is_open) {
- (*(dpriv->device))->USBDeviceClose (dpriv->device);
+ (*dpriv->device)->USBDeviceClose (dpriv->device);
}
priv->is_open = false;
if (priv->is_open) {
/* close the device */
- kresult = (*(dpriv->device))->USBDeviceClose(dpriv->device);
+ kresult = (*dpriv->device)->USBDeviceClose(dpriv->device);
if (kresult != kIOReturnSuccess) {
/* Log the fact that we had a problem closing the file, however failing a
* close isn't really an error, so return success anyway */
if (dev_handle->claimed_interfaces & (1U << i))
darwin_release_interface (dev_handle, i);
- kresult = (*(dpriv->device))->SetConfiguration (dpriv->device, (UInt8)config);
+ kresult = (*dpriv->device)->SetConfiguration (dpriv->device, (UInt8)config);
if (kresult != kIOReturnSuccess)
return darwin_to_libusb (kresult);
return LIBUSB_SUCCESS;
}
-static IOReturn darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_service_t *usbInterfacep) {
+static IOReturn darwin_get_interface (usb_device_t darwin_device, uint8_t ifc, io_service_t *usbInterfacep) {
IOUSBFindInterfaceRequest request;
IOReturn kresult;
io_iterator_t interface_iterator;
request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
- kresult = (*(darwin_device))->CreateInterfaceIterator(darwin_device, &request, &interface_iterator);
+ kresult = (*darwin_device)->CreateInterfaceIterator(darwin_device, &request, &interface_iterator);
if (kresult != kIOReturnSuccess)
return kresult;
return kIOReturnSuccess;
}
+static const struct libusb_interface_descriptor *get_interface_descriptor_by_number(struct libusb_device_handle *dev_handle, struct libusb_config_descriptor *conf_desc, int iface, uint8_t altsetting)
+{
+ int i;
+
+ for (i = 0; i < conf_desc->bNumInterfaces; i++) {
+ if (altsetting < conf_desc->interface[i].num_altsetting && conf_desc->interface[i].altsetting[altsetting].bInterfaceNumber == iface) {
+ return &conf_desc->interface[i].altsetting[altsetting];
+ }
+ }
+
+ usbi_err(HANDLE_CTX(dev_handle), "interface %d with altsetting %d not found for device", iface, (int)altsetting);
+ return NULL;
+}
+
static enum libusb_error get_endpoints (struct libusb_device_handle *dev_handle, uint8_t iface) {
struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle);
/* current interface */
struct darwin_interface *cInterface = &priv->interfaces[iface];
-#if InterfaceVersion >= 550
- IOUSBEndpointProperties pipeProperties = {.bVersion = kUSBEndpointPropertiesVersion3};
-#else
- UInt8 dont_care1, dont_care3;
- UInt16 dont_care2;
-#endif
-
IOReturn kresult;
-
- UInt8 numep, direction, number;
+ uint8_t numep;
int rc;
struct libusb_context *ctx = HANDLE_CTX (dev_handle);
-
usbi_dbg (ctx, "building table of endpoints.");
/* retrieve the total number of endpoints on this interface */
- kresult = (*(cInterface->interface))->GetNumEndpoints(cInterface->interface, &numep);
+ kresult = (*IOINTERFACE(cInterface))->GetNumEndpoints(IOINTERFACE(cInterface), &numep);
if (kresult != kIOReturnSuccess) {
usbi_err (ctx, "can't get number of endpoints for interface: %s", darwin_error_str(kresult));
return darwin_to_libusb (kresult);
/* iterate through pipe references */
for (UInt8 i = 1 ; i <= numep ; i++) {
-#if InterfaceVersion >= 550
- kresult = (*(cInterface->interface))->GetPipePropertiesV3 (cInterface->interface, i, &pipeProperties);
- number = pipeProperties.bEndpointNumber;
- direction = pipeProperties.bDirection;
-#else
- kresult = (*(cInterface->interface))->GetPipeProperties(cInterface->interface, i, &direction, &number, &dont_care1,
- &dont_care2, &dont_care3);
-#endif
+ darwin_pipe_properties_t pipe_properties;
+ kresult = darwin_get_pipe_properties(cInterface, i, &pipe_properties);
if (kresult != kIOReturnSuccess) {
/* probably a buggy device. try to get the endpoint address from the descriptors */
struct libusb_config_descriptor *config;
+ const struct libusb_interface_descriptor *if_desc;
const struct libusb_endpoint_descriptor *endpoint_desc;
UInt8 alt_setting;
- kresult = (*(cInterface->interface))->GetAlternateSetting (cInterface->interface, &alt_setting);
+ kresult = (*IOINTERFACE(cInterface))->GetAlternateSetting (IOINTERFACE(cInterface), &alt_setting);
if (kresult != kIOReturnSuccess) {
usbi_err (HANDLE_CTX (dev_handle), "can't get alternate setting for interface");
return darwin_to_libusb (kresult);
return rc;
}
- if (iface >= config->bNumInterfaces) {
- usbi_err (HANDLE_CTX (dev_handle), "interface %d out of range for device", iface);
+ if_desc = get_interface_descriptor_by_number (dev_handle, config, iface, alt_setting);
+ if (if_desc == NULL) {
+ libusb_free_config_descriptor (config);
return LIBUSB_ERROR_NOT_FOUND;
}
- endpoint_desc = config->interface[iface].altsetting[alt_setting].endpoint + i - 1;
+
+ endpoint_desc = if_desc->endpoint + i - 1;
cInterface->endpoint_addrs[i - 1] = endpoint_desc->bEndpointAddress;
+ libusb_free_config_descriptor (config);
} else {
- cInterface->endpoint_addrs[i - 1] = (UInt8)(((kUSBIn == direction) << kUSBRqDirnShift) | (number & LIBUSB_ENDPOINT_ADDRESS_MASK));
+ cInterface->endpoint_addrs[i - 1] = (UInt8)(((kUSBIn == pipe_properties.direction) << kUSBRqDirnShift) |
+ (pipe_properties.number & LIBUSB_ENDPOINT_ADDRESS_MASK));
}
usbi_dbg (ctx, "interface: %i pipe %i: dir: %i number: %i", iface, i, cInterface->endpoint_addrs[i - 1] >> kUSBRqDirnShift,
/* Do the actual claim */
kresult = (*plugInInterface)->QueryInterface(plugInInterface,
- CFUUIDGetUUIDBytes(InterfaceInterfaceID),
- (LPVOID)&cInterface->interface);
+ CFUUIDGetUUIDBytes(get_interface_interface_id()),
+ (LPVOID)&IOINTERFACE(cInterface));
/* We no longer need the intermediate plug-in */
/* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */
(*plugInInterface)->Release (plugInInterface);
- if (kresult != kIOReturnSuccess || !cInterface->interface) {
+ if (kresult != kIOReturnSuccess) {
usbi_err (ctx, "QueryInterface: %s", darwin_error_str(kresult));
return darwin_to_libusb (kresult);
}
+ if (!IOINTERFACE(cInterface)) {
+ usbi_err (ctx, "QueryInterface: returned null interface");
+ return LIBUSB_ERROR_OTHER;
+ }
/* claim the interface */
- kresult = (*(cInterface->interface))->USBInterfaceOpen(cInterface->interface);
+ kresult = (*IOINTERFACE(cInterface))->USBInterfaceOpen(IOINTERFACE(cInterface));
if (kresult != kIOReturnSuccess) {
usbi_info (ctx, "USBInterfaceOpen: %s", darwin_error_str(kresult));
return darwin_to_libusb (kresult);
cInterface->cfSource = NULL;
/* create async event source */
- kresult = (*(cInterface->interface))->CreateInterfaceAsyncEventSource (cInterface->interface, &cInterface->cfSource);
+ kresult = (*IOINTERFACE(cInterface))->CreateInterfaceAsyncEventSource (IOINTERFACE(cInterface), &cInterface->cfSource);
if (kresult != kIOReturnSuccess) {
usbi_err (ctx, "could not create async event source");
struct darwin_interface *cInterface = &priv->interfaces[iface];
/* Check to see if an interface is open */
- if (!cInterface->interface)
+ if (!IOINTERFACE(cInterface)) {
return LIBUSB_SUCCESS;
+ }
/* clean up endpoint data */
cInterface->num_endpoints = 0;
cInterface->cfSource = NULL;
}
- kresult = (*(cInterface->interface))->USBInterfaceClose(cInterface->interface);
+ kresult = (*IOINTERFACE(cInterface))->USBInterfaceClose(IOINTERFACE(cInterface));
if (kresult != kIOReturnSuccess)
usbi_warn (HANDLE_CTX (dev_handle), "USBInterfaceClose: %s", darwin_error_str(kresult));
- kresult = (*(cInterface->interface))->Release(cInterface->interface);
+ kresult = (*IOINTERFACE(cInterface))->Release(IOINTERFACE(cInterface));
if (kresult != kIOReturnSuccess)
usbi_warn (HANDLE_CTX (dev_handle), "Release: %s", darwin_error_str(kresult));
- cInterface->interface = (usb_interface_t **) IO_OBJECT_NULL;
+ IOINTERFACE(cInterface) = NULL;
return darwin_to_libusb (kresult);
}
IOReturn kresult;
uint8_t current_alt_setting;
- kresult = (*(cInterface->interface))->GetAlternateSetting (cInterface->interface, ¤t_alt_setting);
+ kresult = (*IOINTERFACE(cInterface))->GetAlternateSetting (IOINTERFACE(cInterface), ¤t_alt_setting);
if (kresult == kIOReturnSuccess && altsetting != current_alt_setting) {
return LIBUSB_ERROR_PIPE;
}
/* current interface */
struct darwin_interface *cInterface = &priv->interfaces[iface];
- if (!cInterface->interface)
+ if (!IOINTERFACE(cInterface)) {
return LIBUSB_ERROR_NO_DEVICE;
+ }
- kresult = (*(cInterface->interface))->SetAlternateInterface (cInterface->interface, altsetting);
+ kresult = (*IOINTERFACE(cInterface))->SetAlternateInterface (IOINTERFACE(cInterface), altsetting);
if (kresult == kIOReturnSuccess) {
/* update the list of endpoints */
ret = get_endpoints (dev_handle, iface);
}
/* newer versions of darwin support clearing additional bits on the device's endpoint */
- kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef);
+ kresult = (*IOINTERFACE(cInterface))->ClearPipeStallBothEnds(IOINTERFACE(cInterface), pipeRef);
if (kresult != kIOReturnSuccess)
usbi_warn (HANDLE_CTX (dev_handle), "ClearPipeStall: %s", darwin_error_str (kresult));
cached_configurations = alloca (sizeof (*cached_configurations) * descriptor.bNumConfigurations);
for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) {
- (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration);
+ (*dpriv->device)->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration);
memcpy (cached_configurations + i, cached_configuration, sizeof (cached_configurations[i]));
}
/* if we need to release capture */
- if (HAS_CAPTURE_DEVICE()) {
+ if (get_running_version() >= 101000) {
if (capture) {
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
options |= kUSBReEnumerateCaptureDeviceMask;
}
/* from macOS 10.11 ResetDevice no longer does anything so just use USBDeviceReEnumerate */
- kresult = (*(dpriv->device))->USBDeviceReEnumerate (dpriv->device, options);
+ kresult = (*dpriv->device)->USBDeviceReEnumerate (dpriv->device, options);
if (kresult != kIOReturnSuccess) {
usbi_err (ctx, "USBDeviceReEnumerate: %s", darwin_error_str (kresult));
dpriv->in_reenumerate = false;
}
for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) {
- (void) (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration);
+ (void) (*dpriv->device)->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration);
if (memcmp (cached_configuration, cached_configurations + i, sizeof (cached_configurations[i]))) {
usbi_dbg (ctx, "darwin/reenumerate_device: configuration descriptor %d changed", i);
return LIBUSB_ERROR_NOT_FOUND;
#if !defined(TARGET_OS_OSX) || TARGET_OS_OSX == 1
if (dpriv->capture_count > 0) {
/* we have to use ResetDevice as USBDeviceReEnumerate() loses the authorization for capture */
- kresult = (*(dpriv->device))->ResetDevice (dpriv->device);
+ kresult = (*dpriv->device)->ResetDevice (dpriv->device);
ret = darwin_to_libusb (kresult);
} else {
ret = darwin_reenumerate_device (dev_handle, false);
if (dpriv->dev) {
/* need to hold the lock in case this is the last reference to the device */
- usbi_mutex_lock(&darwin_cached_devices_lock);
+ usbi_mutex_lock(&darwin_cached_devices_mutex);
darwin_deref_cached_device (dpriv->dev);
dpriv->dev = NULL;
- usbi_mutex_unlock(&darwin_cached_devices_lock);
+ usbi_mutex_unlock(&darwin_cached_devices_mutex);
}
}
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
IOReturn ret;
- uint8_t transferType;
uint8_t pipeRef;
- uint16_t maxPacketSize;
struct darwin_interface *cInterface;
-#if InterfaceVersion >= 550
- IOUSBEndpointProperties pipeProperties = {.bVersion = kUSBEndpointPropertiesVersion3};
-#else
- /* None of the values below are used in libusb for bulk transfers */
- uint8_t direction, number, interval;
-#endif
+ darwin_pipe_properties_t pipe_properties;
if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) {
usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface");
return LIBUSB_ERROR_NOT_FOUND;
}
-#if InterfaceVersion >= 550
- ret = (*(cInterface->interface))->GetPipePropertiesV3 (cInterface->interface, pipeRef, &pipeProperties);
-
- transferType = pipeProperties.bTransferType;
- maxPacketSize = pipeProperties.wMaxPacketSize;
-#else
- ret = (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
- &transferType, &maxPacketSize, &interval);
-#endif
-
- if (ret) {
+ ret = darwin_get_pipe_properties(cInterface, pipeRef, &pipe_properties);
+ if (kIOReturnSuccess != ret) {
usbi_err (TRANSFER_CTX (transfer), "bulk transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out",
darwin_error_str(ret), ret);
return darwin_to_libusb (ret);
}
- if (0 != (transfer->length % maxPacketSize)) {
+ if (0 != (transfer->length % pipe_properties.max_packet_size)) {
/* do not need a zero packet */
transfer->flags &= ~LIBUSB_TRANSFER_ADD_ZERO_PACKET;
}
/* submit the request */
/* timeouts are unavailable on interrupt endpoints */
- if (transferType == kUSBInterrupt) {
+ if (pipe_properties.transfer_type == kUSBInterrupt) {
if (IS_XFERIN(transfer))
- ret = (*(cInterface->interface))->ReadPipeAsync(cInterface->interface, pipeRef, transfer->buffer,
- (UInt32)transfer->length, darwin_async_io_callback, itransfer);
+ ret = (*IOINTERFACE(cInterface))->ReadPipeAsync(IOINTERFACE(cInterface), pipeRef, transfer->buffer,
+ (UInt32)transfer->length, darwin_async_io_callback, itransfer);
else
- ret = (*(cInterface->interface))->WritePipeAsync(cInterface->interface, pipeRef, transfer->buffer,
- (UInt32)transfer->length, darwin_async_io_callback, itransfer);
+ ret = (*IOINTERFACE(cInterface))->WritePipeAsync(IOINTERFACE(cInterface), pipeRef, transfer->buffer,
+ (UInt32)transfer->length, darwin_async_io_callback, itransfer);
} else {
itransfer->timeout_flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT;
if (IS_XFERIN(transfer))
- ret = (*(cInterface->interface))->ReadPipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer,
- (UInt32)transfer->length, transfer->timeout, transfer->timeout,
- darwin_async_io_callback, itransfer);
+ ret = (*IOINTERFACE(cInterface))->ReadPipeAsyncTO(IOINTERFACE(cInterface), pipeRef, transfer->buffer,
+ (UInt32)transfer->length, transfer->timeout, transfer->timeout,
+ darwin_async_io_callback, itransfer);
else
- ret = (*(cInterface->interface))->WritePipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer,
- (UInt32)transfer->length, transfer->timeout, transfer->timeout,
- darwin_async_io_callback, itransfer);
+ ret = (*IOINTERFACE(cInterface))->WritePipeAsyncTO(IOINTERFACE(cInterface), pipeRef, transfer->buffer,
+ (UInt32)transfer->length, transfer->timeout, transfer->timeout,
+ darwin_async_io_callback, itransfer);
}
if (ret)
return darwin_to_libusb (ret);
}
-#if InterfaceVersion >= 550
+#if MAX_INTERFACE_VERSION >= 550
static int submit_stream_transfer(struct usbi_transfer *itransfer) {
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct darwin_interface *cInterface;
return LIBUSB_ERROR_NOT_FOUND;
}
+ if (get_interface_interface_version() < 550) {
+ usbi_err (TRANSFER_CTX(transfer), "IOUSBFamily version %d does not support bulk stream transfers",
+ get_interface_interface_version());
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ }
+
itransfer->timeout_flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT;
if (IS_XFERIN(transfer))
- ret = (*(cInterface->interface))->ReadStreamsPipeAsyncTO(cInterface->interface, pipeRef, itransfer->stream_id,
- transfer->buffer, (UInt32)transfer->length, transfer->timeout,
- transfer->timeout, darwin_async_io_callback, itransfer);
+ ret = (*IOINTERFACE_V(cInterface, 550))->ReadStreamsPipeAsyncTO(IOINTERFACE(cInterface), pipeRef, itransfer->stream_id,
+ transfer->buffer, (UInt32)transfer->length, transfer->timeout,
+ transfer->timeout, darwin_async_io_callback, itransfer);
else
- ret = (*(cInterface->interface))->WriteStreamsPipeAsyncTO(cInterface->interface, pipeRef, itransfer->stream_id,
- transfer->buffer, (UInt32)transfer->length, transfer->timeout,
- transfer->timeout, darwin_async_io_callback, itransfer);
+ ret = (*IOINTERFACE_V(cInterface, 550))->WriteStreamsPipeAsyncTO(IOINTERFACE(cInterface), pipeRef, itransfer->stream_id,
+ transfer->buffer, (UInt32)transfer->length, transfer->timeout,
+ transfer->timeout, darwin_async_io_callback, itransfer);
if (ret)
usbi_err (TRANSFER_CTX (transfer), "bulk stream transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out",
struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer);
IOReturn kresult;
- uint8_t pipeRef, interval;
+ uint8_t pipeRef;
UInt64 frame;
AbsoluteTime atTime;
int i;
-#if InterfaceVersion >= 550
- IOUSBEndpointProperties pipeProperties = {.bVersion = kUSBEndpointPropertiesVersion3};
-#else
- /* None of the values below are used in libusb for iso transfers */
- uint8_t direction, number, transferType;
- uint16_t maxPacketSize;
-#endif
-
+ darwin_pipe_properties_t pipe_properties;
struct darwin_interface *cInterface;
/* construct an array of IOUSBIsocFrames, reuse the old one if the sizes are the same */
}
/* determine the properties of this endpoint and the speed of the device */
-#if InterfaceVersion >= 550
- kresult = (*(cInterface->interface))->GetPipePropertiesV3 (cInterface->interface, pipeRef, &pipeProperties);
- interval = pipeProperties.bInterval;
-#else
- kresult = (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
- &transferType, &maxPacketSize, &interval);
-#endif
+ kresult = darwin_get_pipe_properties(cInterface, pipeRef, &pipe_properties);
if (kresult != kIOReturnSuccess) {
usbi_err (TRANSFER_CTX (transfer), "failed to get pipe properties: %d", kresult);
free(tpriv->isoc_framelist);
}
/* Last but not least we need the bus frame number */
- kresult = (*(cInterface->interface))->GetBusFrameNumber(cInterface->interface, &frame, &atTime);
+ kresult = (*IOINTERFACE(cInterface))->GetBusFrameNumber(IOINTERFACE(cInterface), &frame, &atTime);
if (kresult != kIOReturnSuccess) {
usbi_err (TRANSFER_CTX (transfer), "failed to get bus frame number: %d", kresult);
free(tpriv->isoc_framelist);
/* submit the request */
if (IS_XFERIN(transfer))
- kresult = (*(cInterface->interface))->ReadIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame,
- (UInt32)transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback,
- itransfer);
+ kresult = (*IOINTERFACE(cInterface))->ReadIsochPipeAsync(IOINTERFACE(cInterface), pipeRef, transfer->buffer, frame,
+ (UInt32)transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback,
+ itransfer);
else
- kresult = (*(cInterface->interface))->WriteIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame,
- (UInt32)transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback,
- itransfer);
+ kresult = (*IOINTERFACE(cInterface))->WriteIsochPipeAsync(IOINTERFACE(cInterface), pipeRef, transfer->buffer, frame,
+ (UInt32)transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback,
+ itransfer);
if (LIBUSB_SPEED_FULL == transfer->dev_handle->dev->speed)
/* Full speed */
- cInterface->frames[transfer->endpoint] = frame + (UInt32)transfer->num_iso_packets * (1U << (interval - 1));
+ cInterface->frames[transfer->endpoint] = frame + (UInt32)transfer->num_iso_packets * (1U << (pipe_properties.interval - 1));
else
/* High/super speed */
- cInterface->frames[transfer->endpoint] = frame + (UInt32)transfer->num_iso_packets * (1U << (interval - 1)) / 8;
+ cInterface->frames[transfer->endpoint] = frame + (UInt32)transfer->num_iso_packets * (1U << (pipe_properties.interval - 1)) / 8;
if (kresult != kIOReturnSuccess) {
usbi_err (TRANSFER_CTX (transfer), "isochronous transfer failed (dir: %s): %s", IS_XFERIN(transfer) ? "In" : "Out",
return LIBUSB_ERROR_NOT_FOUND;
}
- kresult = (*(cInterface->interface))->ControlRequestAsyncTO (cInterface->interface, pipeRef, &(tpriv->req), darwin_async_io_callback, itransfer);
+ kresult = (*IOINTERFACE(cInterface))->ControlRequestAsyncTO (IOINTERFACE(cInterface), pipeRef,
+ &(tpriv->req), darwin_async_io_callback, itransfer);
} else
/* control request on endpoint 0 */
- kresult = (*(dpriv->device))->DeviceRequestAsyncTO(dpriv->device, &(tpriv->req), darwin_async_io_callback, itransfer);
+ kresult = (*dpriv->device)->DeviceRequestAsyncTO(dpriv->device, &(tpriv->req), darwin_async_io_callback, itransfer);
if (kresult != kIOReturnSuccess)
usbi_err (TRANSFER_CTX (transfer), "control request failed: %s", darwin_error_str(kresult));
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
return submit_iso_transfer(itransfer);
case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
-#if InterfaceVersion >= 550
+#if MAX_INTERFACE_VERSION >= 550
return submit_stream_transfer(itransfer);
#else
usbi_err (TRANSFER_CTX(transfer), "IOUSBFamily version does not support bulk stream transfers");
usbi_warn (ITRANSFER_CTX (itransfer), "aborting all transactions control pipe");
- if (!dpriv->device)
+ if (!dpriv->device) {
return LIBUSB_ERROR_NO_DEVICE;
+ }
- kresult = (*(dpriv->device))->USBDeviceAbortPipeZero (dpriv->device);
+ kresult = (*dpriv->device)->USBDeviceAbortPipeZero (dpriv->device);
return darwin_to_libusb (kresult);
}
return LIBUSB_ERROR_NOT_FOUND;
}
- if (!dpriv->device)
+ if (!dpriv->device) {
return LIBUSB_ERROR_NO_DEVICE;
+ }
usbi_warn (ctx, "aborting all transactions on interface %d pipe %d", iface, pipeRef);
/* abort transactions */
-#if InterfaceVersion >= 550
- if (LIBUSB_TRANSFER_TYPE_BULK_STREAM == transfer->type)
- (*(cInterface->interface))->AbortStreamsPipe (cInterface->interface, pipeRef, itransfer->stream_id);
- else
+#if MAX_INTERFACE_VERSION >= 550
+ if (LIBUSB_TRANSFER_TYPE_BULK_STREAM == transfer->type && get_interface_interface_version() >= 550) {
+ kresult = (*IOINTERFACE_V(cInterface, 550))->AbortStreamsPipe (IOINTERFACE(cInterface), pipeRef, itransfer->stream_id);
+ } else
#endif
- (*(cInterface->interface))->AbortPipe (cInterface->interface, pipeRef);
+ {
+ kresult = (*IOINTERFACE(cInterface))->AbortPipe (IOINTERFACE(cInterface), pipeRef);
+ }
- usbi_dbg (ctx, "calling clear pipe stall to clear the data toggle bit");
- /* newer versions of darwin support clearing additional bits on the device's endpoint */
- kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef);
+ if (get_interface_interface_version() <= 245) {
+ /* with older releases of IOUSBFamily the OS always clears the host side data toggle. for
+ consistency also clear the data toggle on the device. */
+ usbi_dbg (ctx, "calling ClearPipeStallBothEnds to clear the data toggle bit");
+ kresult = (*IOINTERFACE(cInterface))->ClearPipeStallBothEnds(IOINTERFACE(cInterface), pipeRef);
+ }
return darwin_to_libusb (kresult);
}
(void) ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface);
- (*(cInterface->interface))->WritePipe (cInterface->interface, pipeRef, transfer->buffer, 0);
+ (*IOINTERFACE(cInterface))->WritePipe (IOINTERFACE(cInterface), pipeRef, transfer->buffer, 0);
}
tpriv->result = result;
return usbi_handle_transfer_completion (itransfer, darwin_transfer_status (itransfer, tpriv->result));
}
-#if !defined(HAVE_CLOCK_GETTIME)
void usbi_get_monotonic_time(struct timespec *tp) {
- mach_timespec_t sys_time;
+/* Check if the SDK is new enough to declare clock_gettime(), and the deployment target is at least 10.12. */
+#if ((MAC_OS_X_VERSION_MAX_ALLOWED >= 101200) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 101200))
+ clock_gettime(CLOCK_MONOTONIC, tp);
+#else
+ mach_timebase_info_data_t machTimeBaseInfo;
+ mach_timebase_info(&machTimeBaseInfo);
- /* use system boot time as reference for the monotonic clock */
- clock_get_time (clock_monotonic, &sys_time);
+ uint64_t uptime = mach_absolute_time();
+ uint64_t uptimeNano = uptime * machTimeBaseInfo.numer / machTimeBaseInfo.denom;
- tp->tv_sec = sys_time.tv_sec;
- tp->tv_nsec = sys_time.tv_nsec;
+ uint64_t uptimeSeconds = uptimeNano / NSEC_PER_SEC;
+ uint64_t uptimeNanoRemainder = uptimeNano - (uptimeSeconds * NSEC_PER_SEC);
+
+ tp->tv_sec = uptimeSeconds;
+ tp->tv_nsec = uptimeNanoRemainder;
+#endif
}
void usbi_get_real_time(struct timespec *tp) {
- mach_timespec_t sys_time;
-
- /* CLOCK_REALTIME represents time since the epoch */
- clock_get_time (clock_realtime, &sys_time);
-
- tp->tv_sec = sys_time.tv_sec;
- tp->tv_nsec = sys_time.tv_nsec;
-}
+/* Check if the SDK is new enough to declare clock_gettime(), and the deployment target is at least 10.12. */
+#if ((MAC_OS_X_VERSION_MAX_ALLOWED >= 101200) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 101200))
+ clock_gettime(CLOCK_REALTIME, tp);
+#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ tp->tv_sec = tv.tv_sec;
+ tp->tv_nsec = tv.tv_usec * NSEC_PER_USEC;
#endif
+}
-#if InterfaceVersion >= 550
+#if MAX_INTERFACE_VERSION >= 550
static int darwin_alloc_streams (struct libusb_device_handle *dev_handle, uint32_t num_streams, unsigned char *endpoints,
int num_endpoints) {
struct darwin_interface *cInterface;
return rc;
}
- (*(cInterface->interface))->SupportsStreams (cInterface->interface, pipeRef, &supportsStreams);
+ (*IOINTERFACE_V(cInterface, 550))->SupportsStreams (IOINTERFACE(cInterface), pipeRef, &supportsStreams);
if (num_streams > supportsStreams)
num_streams = supportsStreams;
}
for (i = 0 ; i < num_endpoints ; ++i) {
(void) ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface);
- rc = (*(cInterface->interface))->CreateStreams (cInterface->interface, pipeRef, num_streams);
+ rc = (*IOINTERFACE_V(cInterface, 550))->CreateStreams (IOINTERFACE(cInterface), pipeRef, num_streams);
if (kIOReturnSuccess != rc)
return darwin_to_libusb(rc);
}
if (0 != (rc = ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface)))
return rc;
- (*(cInterface->interface))->SupportsStreams (cInterface->interface, pipeRef, &supportsStreams);
+ (*IOINTERFACE_V(cInterface, 550))->SupportsStreams (IOINTERFACE(cInterface), pipeRef, &supportsStreams);
if (0 == supportsStreams)
return LIBUSB_ERROR_INVALID_PARAM;
- rc = (*(cInterface->interface))->CreateStreams (cInterface->interface, pipeRef, 0);
+ rc = (*IOINTERFACE_V(cInterface, 550))->CreateStreams (IOINTERFACE(cInterface), pipeRef, 0);
if (kIOReturnSuccess != rc)
return darwin_to_libusb(rc);
}
}
#endif
-#if InterfaceVersion >= 700
+#if MAX_INTERFACE_VERSION >= 700
/* macOS APIs for getting entitlement values */
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
enum libusb_error err;
- usbi_mutex_lock(&darwin_cached_devices_lock);
- (*(dpriv->device))->Release(dpriv->device);
- dpriv->device = darwin_device_from_service (HANDLE_CTX (dev_handle), dpriv->service);
- if (!dpriv->device) {
- err = LIBUSB_ERROR_NO_DEVICE;
- } else {
- err = LIBUSB_SUCCESS;
- }
- usbi_mutex_unlock(&darwin_cached_devices_lock);
+ usbi_mutex_lock(&darwin_cached_devices_mutex);
+ (*dpriv->device)->Release(dpriv->device);
+ err = darwin_device_from_service (HANDLE_CTX (dev_handle), dpriv->service, &dpriv->device);
+ usbi_mutex_unlock(&darwin_cached_devices_mutex);
return err;
}
enum libusb_error err;
struct libusb_context *ctx = HANDLE_CTX (dev_handle);
- if (HAS_CAPTURE_DEVICE()) {
- } else {
+ if (get_interface_interface_version() < 700) {
return LIBUSB_ERROR_NOT_SUPPORTED;
}
UNUSED(interface);
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
- if (HAS_CAPTURE_DEVICE()) {
- } else {
+ if (get_interface_interface_version() < 700) {
return LIBUSB_ERROR_NOT_SUPPORTED;
}
.clear_halt = darwin_clear_halt,
.reset_device = darwin_reset_device,
-#if InterfaceVersion >= 550
+#if MAX_INTERFACE_VERSION >= 550
.alloc_streams = darwin_alloc_streams,
.free_streams = darwin_free_streams,
#endif
.kernel_driver_active = darwin_kernel_driver_active,
-#if InterfaceVersion >= 700
+#if MAX_INTERFACE_VERSION >= 700
.detach_kernel_driver = darwin_detach_kernel_driver,
.attach_kernel_driver = darwin_attach_kernel_driver,
.claim_interface = darwin_capture_claim_interface,
/*
* darwin backend for libusb 1.0
- * Copyright © 2008-2019 Nathan Hjelm <hjelmn@users.sourceforge.net>
- * Copyright © 2019 Google LLC. All rights reserved.
+ * Copyright © 2008-2023 Nathan Hjelm <hjelmn@users.sourceforge.net>
+ * Copyright © 2019-2023 Google LLC. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
/* IOUSBInterfaceInferface */
-/* New in OS 10.12.0. */
-#if defined (kIOUSBInterfaceInterfaceID800)
-
-#define usb_interface_t IOUSBInterfaceInterface800
-#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID800
-#define InterfaceVersion 800
-
-/* New in OS 10.10.0. */
-#elif defined (kIOUSBInterfaceInterfaceID700)
-
-#define usb_interface_t IOUSBInterfaceInterface700
-#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID700
-#define InterfaceVersion 700
-
-/* New in OS 10.9.0. */
-#elif defined (kIOUSBInterfaceInterfaceID650)
-
-#define usb_interface_t IOUSBInterfaceInterface650
-#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID650
-#define InterfaceVersion 650
-
-/* New in OS 10.8.2 but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBInterfaceInterfaceID550)
-
-#define usb_interface_t IOUSBInterfaceInterface550
-#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID550
-#define InterfaceVersion 550
-
-/* New in OS 10.7.3 but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBInterfaceInterfaceID500)
-
-#define usb_interface_t IOUSBInterfaceInterface500
-#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID500
-#define InterfaceVersion 500
-
-/* New in OS 10.5.0. */
-#elif defined (kIOUSBInterfaceInterfaceID300)
-
-#define usb_interface_t IOUSBInterfaceInterface300
-#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID300
-#define InterfaceVersion 300
-
-/* New in OS 10.4.5 (or 10.4.6?) but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBInterfaceInterfaceID245)
-
-#define usb_interface_t IOUSBInterfaceInterface245
-#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID245
-#define InterfaceVersion 245
-
-/* New in OS 10.4.0. */
-#elif defined (kIOUSBInterfaceInterfaceID220)
-
-#define usb_interface_t IOUSBInterfaceInterface220
-#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220
-#define InterfaceVersion 220
-
+#if defined(kIOUSBInterfaceInterfaceID800)
+#define MAX_INTERFACE_VERSION 800
+#elif defined(kIOUSBInterfaceInterfaceID700)
+#define MAX_INTERFACE_VERSION 700
+#elif defined(kIOUSBInterfaceInterfaceID650)
+#define MAX_INTERFACE_VERSION 650
+#elif defined(kIOUSBInterfaceInterfaceID550)
+#define MAX_INTERFACE_VERSION 550
+#elif defined(kIOUSBInterfaceInterfaceID245)
+#define MAX_INTERFACE_VERSION 245
#else
-
-#error "IOUSBFamily is too old. Please upgrade your SDK and/or deployment target"
-
+#define MAX_INTERFACE_VERSION 220
#endif
-/* IOUSBDeviceInterface */
-
-/* New in OS 10.9.0. */
-#if defined (kIOUSBDeviceInterfaceID650)
-
-#define usb_device_t IOUSBDeviceInterface650
-#define DeviceInterfaceID kIOUSBDeviceInterfaceID650
-#define DeviceVersion 650
-
-/* New in OS 10.7.3 but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBDeviceInterfaceID500)
-
-#define usb_device_t IOUSBDeviceInterface500
-#define DeviceInterfaceID kIOUSBDeviceInterfaceID500
-#define DeviceVersion 500
-
-/* New in OS 10.5.4 but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBDeviceInterfaceID320)
-
-#define usb_device_t IOUSBDeviceInterface320
-#define DeviceInterfaceID kIOUSBDeviceInterfaceID320
-#define DeviceVersion 320
-
-/* New in OS 10.5.0. */
-#elif defined (kIOUSBDeviceInterfaceID300)
-
-#define usb_device_t IOUSBDeviceInterface300
-#define DeviceInterfaceID kIOUSBDeviceInterfaceID300
-#define DeviceVersion 300
+/* set to the minimum version and casted up as needed. */
+typedef IOUSBInterfaceInterface220 **usb_interface_t;
-/* New in OS 10.4.5 (or 10.4.6?) but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBDeviceInterfaceID245)
+#define IOINTERFACE0(darwin_interface, version) ((IOUSBInterfaceInterface ## version **) (darwin_interface)->interface)
+#define IOINTERFACE_V(darwin_interface, version) IOINTERFACE0(darwin_interface, version)
+#define IOINTERFACE(darwin_interface) ((darwin_interface)->interface)
-#define usb_device_t IOUSBDeviceInterface245
-#define DeviceInterfaceID kIOUSBDeviceInterfaceID245
-#define DeviceVersion 245
-
-/* New in OS 10.2.3 but can't test deployment target to that granularity, so round up. */
-#elif defined (kIOUSBDeviceInterfaceID197)
-
-#define usb_device_t IOUSBDeviceInterface197
-#define DeviceInterfaceID kIOUSBDeviceInterfaceID197
-#define DeviceVersion 197
+/* IOUSBDeviceInterface */
+#if defined(kIOUSBDeviceInterfaceID650)
+#define MAX_DEVICE_VERSION 650
+#elif defined(kIOUSBDeviceInterfaceID500)
+#define MAX_DEVICE_VERSION 500
+#elif defined(kIOUSBDeviceInterfaceID320)
+#define MAX_DEVICE_VERSION 320
+#elif defined(kIOUSBDeviceInterfaceID300)
+#define MAX_DEVICE_VERSION 300
+#elif defined(kIOUSBDeviceInterfaceID245)
+#define MAX_DEVICE_VERSION 245
#else
+#define MAX_DEVICE_VERSION 197
+#endif
-#error "IOUSBFamily is too old. Please upgrade your SDK and/or deployment target"
+/* set to the minimum version and casted up as needed */
+typedef IOUSBDeviceInterface197 **usb_device_t;
-#endif
+#define IODEVICE0(darwin_device, version) ((IOUSBDeviceInterface ## version **)(darwin_device))
+#define IODEVICE_V(darwin_device, version) IODEVICE0(darwin_device, version)
#if !defined(kIOUSBHostInterfaceClassName)
#define kIOUSBHostInterfaceClassName "IOUSBHostInterface"
#define IO_OBJECT_NULL ((io_object_t) 0)
#endif
-/* Testing availability */
-#ifndef __has_builtin
- #define __has_builtin(x) 0 // Compatibility with non-clang compilers.
-#endif
-#if __has_builtin(__builtin_available)
- #define HAS_CAPTURE_DEVICE() __builtin_available(macOS 10.10, *)
-#else
- #define HAS_CAPTURE_DEVICE() 0
-#endif
+/* returns the current macOS version in a format similar to the
+ * MAC_OS_X_VERSION_MIN_REQUIRED macro.
+ * Examples:
+ * 10.1.5 -> 100105
+ * 13.3.0 -> 130300
+ */
+uint32_t get_running_version(void);
typedef IOCFPlugInInterface *io_cf_plugin_ref_t;
typedef IONotificationPortRef io_notification_port_t;
UInt64 session;
USBDeviceAddress address;
char sys_path[21];
- usb_device_t **device;
+ usb_device_t device;
io_service_t service;
int open_count;
UInt8 first_config, active_config, port;
CFRunLoopSourceRef cfSource;
struct darwin_interface {
- usb_interface_t **interface;
+ usb_interface_t interface;
uint8_t num_endpoints;
CFRunLoopSourceRef cfSource;
uint64_t frames[256];
#ifdef HAVE_TIMERFD
#include <sys/timerfd.h>
#endif
+
+#ifdef __EMSCRIPTEN__
+/* On Emscripten `pipe` does not conform to the spec and does not block
+ * until events are available, which makes it unusable for event system
+ * and often results in deadlocks when `pipe` is in a loop like it is
+ * in libusb.
+ *
+ * Therefore use a custom event system based on browser event emitters. */
+#include <emscripten.h>
+#include <emscripten/atomic.h>
+#include <emscripten/threading.h>
+
+EM_ASYNC_JS(void, em_libusb_wait_async, (const _Atomic int* ptr, int expected_value, int timeout), {
+ await Atomics.waitAsync(HEAP32, ptr >> 2, expected_value, timeout).value;
+});
+
+static void em_libusb_wait(const _Atomic int *ptr, int expected_value, int timeout)
+{
+ if (emscripten_is_main_runtime_thread()) {
+ em_libusb_wait_async(ptr, expected_value, timeout);
+ } else {
+ emscripten_atomic_wait_u32((int*)ptr, expected_value, 1000000LL * timeout);
+ }
+}
+#endif
#include <unistd.h>
#ifdef HAVE_EVENTFD
r = write(EVENT_WRITE_FD(event), &dummy, sizeof(dummy));
if (r != sizeof(dummy))
usbi_warn(NULL, "event write failed");
+#ifdef __EMSCRIPTEN__
+ event->has_event = 1;
+ emscripten_atomic_notify(&event->has_event, EMSCRIPTEN_NOTIFY_ALL_WAITERS);
+#endif
}
void usbi_clear_event(usbi_event_t *event)
r = read(EVENT_READ_FD(event), &dummy, sizeof(dummy));
if (r != sizeof(dummy))
usbi_warn(NULL, "event read failed");
+#ifdef __EMSCRIPTEN__
+ event->has_event = 0;
+#endif
}
#ifdef HAVE_TIMERFD
int internal_fds, num_ready;
usbi_dbg(ctx, "poll() %u fds with timeout in %dms", (unsigned int)nfds, timeout_ms);
+#ifdef __EMSCRIPTEN__
+ // Emscripten's poll doesn't actually block, so we need to use an out-of-band
+ // waiting signal.
+ em_libusb_wait(&ctx->event.has_event, 0, timeout_ms);
+ // Emscripten ignores timeout_ms, but set it to 0 for future-proofing in case
+ // they ever implement real poll.
+ timeout_ms = 0;
+#endif
num_ready = poll(fds, nfds, timeout_ms);
usbi_dbg(ctx, "poll() returned %d", num_ready);
if (num_ready == 0) {
#else
typedef struct usbi_event {
int pipefd[2];
+#ifdef __EMSCRIPTEN__
+ _Atomic int has_event;
+#endif
} usbi_event_t;
#define USBI_EVENT_OS_HANDLE(e) ((e)->pipefd[0])
#define USBI_EVENT_POLL_EVENTS POLLIN
#elif defined(__NetBSD__)
# include <lwp.h>
#elif defined(__OpenBSD__)
-# define _BSD_SOURCE
-# include <sys/syscall.h>
# include <unistd.h>
#elif defined(__sun__)
# include <sys/lwp.h>
#elif defined(__NetBSD__)
tid = _lwp_self();
#elif defined(__OpenBSD__)
- /* The following only works with OpenBSD > 5.1 as it requires
- * real thread support. For 5.1 and earlier, -1 is returned. */
- tid = syscall(SYS_getthrid);
+ tid = getthrid();
#elif defined(__sun__)
tid = _lwp_self();
#else
* <li> Download the latest \c strerror.c from:<br>
* https://raw.github.com/libusb/libusb/master/libusb/strerror.c </li>
* <li> Open the file in an UTF-8 capable editor </li>
- * <li> Add the 2 letter <a href="http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes">ISO 639-1</a>
+ * <li> Add the 2 letter <a href="https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes">ISO 639-1</a>
* code for your locale at the end of \c usbi_locale_supported[]<br>
* Eg. for Chinese, you would add "zh" so that:
* \code... usbi_locale_supported[] = { "en", "nl", "fr" };\endcode
* If libusb_setlocale() is not called, all messages will be in English.
*
* The following functions return translatable strings: libusb_strerror().
- * Note that the libusb log messages controlled through libusb_set_debug()
+ * Note that the libusb log messages controlled through LIBUSB_OPTION_LOG_LEVEL
* are not translated, they are always in English.
*
* For POSIX UTF-8 environments if you want libusb to follow the standard
*
* \param locale locale-string in the form of lang[_country_region][.codeset]
* or lang[-region], where lang is a 2 letter ISO 639-1 code
- * \returns LIBUSB_SUCCESS on success
- * \returns LIBUSB_ERROR_INVALID_PARAM if the locale doesn't meet the requirements
- * \returns LIBUSB_ERROR_NOT_FOUND if the requested language is not supported
+ * \returns \ref LIBUSB_SUCCESS on success
+ * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the locale doesn't meet the requirements
+ * \returns \ref LIBUSB_ERROR_NOT_FOUND if the requested language is not supported
* \returns a LIBUSB_ERROR code on other errors
*/
static void LIBUSB_CALL sync_transfer_cb(struct libusb_transfer *transfer)
{
+ usbi_dbg(TRANSFER_CTX(transfer), "actual_length=%d", transfer->actual_length);
+
int *completed = transfer->user_data;
*completed = 1;
- usbi_dbg(TRANSFER_CTX(transfer), "actual_length=%d", transfer->actual_length);
- /* caller interprets result and frees transfer */
+ /*
+ * Right after setting 'completed', another thread might free the transfer, so don't
+ * access it beyond this point. The instantiating thread (not necessarily the
+ * current one) interprets the result and frees the transfer.
+ */
}
static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer)
* before giving up due to no response being received. For an unlimited
* timeout, use value 0.
* \returns on success, the number of bytes actually transferred
- * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out
- * \returns LIBUSB_ERROR_PIPE if the control request was not supported by the
+ * \returns \ref LIBUSB_ERROR_TIMEOUT if the transfer timed out
+ * \returns \ref LIBUSB_ERROR_PIPE if the control request was not supported by the
* device
- * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
- * \returns LIBUSB_ERROR_BUSY if called from event handling context
- * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than
+ * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns \ref LIBUSB_ERROR_BUSY if called from event handling context
+ * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than
* the operating system and/or hardware can support (see \ref asynclimits)
* \returns another LIBUSB_ERROR code on other failures
*/
* timeout, use value 0.
*
* \returns 0 on success (and populates <tt>transferred</tt>)
- * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out (and populates
+ * \returns \ref LIBUSB_ERROR_TIMEOUT if the transfer timed out (and populates
* <tt>transferred</tt>)
- * \returns LIBUSB_ERROR_PIPE if the endpoint halted
- * \returns LIBUSB_ERROR_OVERFLOW if the device offered more data, see
+ * \returns \ref LIBUSB_ERROR_PIPE if the endpoint halted
+ * \returns \ref LIBUSB_ERROR_OVERFLOW if the device offered more data, see
* \ref libusb_packetoverflow
- * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
- * \returns LIBUSB_ERROR_BUSY if called from event handling context
- * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than
+ * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns \ref LIBUSB_ERROR_BUSY if called from event handling context
+ * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than
* the operating system and/or hardware can support (see \ref asynclimits)
* \returns another LIBUSB_ERROR code on other failures
*/
* timeout, use value 0.
*
* \returns 0 on success (and populates <tt>transferred</tt>)
- * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out
- * \returns LIBUSB_ERROR_PIPE if the endpoint halted
- * \returns LIBUSB_ERROR_OVERFLOW if the device offered more data, see
+ * \returns \ref LIBUSB_ERROR_TIMEOUT if the transfer timed out
+ * \returns \ref LIBUSB_ERROR_PIPE if the endpoint halted
+ * \returns \ref LIBUSB_ERROR_OVERFLOW if the device offered more data, see
* \ref libusb_packetoverflow
- * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
- * \returns LIBUSB_ERROR_BUSY if called from event handling context
- * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than
+ * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns \ref LIBUSB_ERROR_BUSY if called from event handling context
+ * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than
* the operating system and/or hardware can support (see \ref asynclimits)
* \returns another LIBUSB_ERROR code on other error
*/
#define LIBUSB_MINOR 0
#endif
#ifndef LIBUSB_MICRO
-#define LIBUSB_MICRO 26
+#define LIBUSB_MICRO 27
#endif
#ifndef LIBUSB_NANO
#define LIBUSB_NANO 0
-#define LIBUSB_NANO 11724
+#define LIBUSB_NANO 11882